Add support for multi-threading of soft updates.

Replace a single soft updates thread with a thread per FFS-filesystem
mount point. The threads are associated with the bufdaemon process.

Reviewed by:  kib
Tested by:    Peter Holm and Scott Long
MFC after:    2 weeks
Sponsored by: Netflix
This commit is contained in:
mckusick 2014-08-04 22:03:58 +00:00
parent 31cee5b11c
commit 52fea1f7b2
3 changed files with 252 additions and 110 deletions

View File

@ -98,7 +98,8 @@ struct buf_ops buf_ops_bio = {
struct buf *buf; /* buffer header pool */
caddr_t unmapped_buf;
static struct proc *bufdaemonproc;
/* Used below and for softdep flushing threads in ufs/ffs/ffs_softdep.c */
struct proc *bufdaemonproc;
static int inmem(struct vnode *vp, daddr_t blkno);
static void vm_hold_free_pages(struct buf *bp, int newbsize);

View File

@ -908,9 +908,9 @@ static void add_to_worklist(struct worklist *, int);
static void wake_worklist(struct worklist *);
static void wait_worklist(struct worklist *, char *);
static void remove_from_worklist(struct worklist *);
static void softdep_flush(void);
static void softdep_flush(void *);
static void softdep_flushjournal(struct mount *);
static int softdep_speedup(void);
static int softdep_speedup(struct ufsmount *);
static void worklist_speedup(struct mount *);
static int journal_mount(struct mount *, struct fs *, struct ucred *);
static void journal_unmount(struct ufsmount *);
@ -962,18 +962,21 @@ static int softdep_count_dependencies(struct buf *bp, int);
/*
* Global lock over all of soft updates.
*/
static struct rwlock lk;
RW_SYSINIT(softdep_lock, &lk, "Softdep Lock");
static struct mtx lk;
MTX_SYSINIT(softdep_lock, &lk, "Global Softdep Lock", MTX_DEF);
#define ACQUIRE_GBLLOCK(lk) mtx_lock(lk)
#define FREE_GBLLOCK(lk) mtx_unlock(lk)
#define GBLLOCK_OWNED(lk) mtx_assert((lk), MA_OWNED)
/*
* Allow per-filesystem soft-updates locking.
* For now all use the same global lock defined above.
* Per-filesystem soft-updates locking.
*/
#define LOCK_PTR(ump) ((ump)->um_softdep->sd_fslock)
#define TRY_ACQUIRE_LOCK(ump) rw_try_wlock((ump)->um_softdep->sd_fslock)
#define ACQUIRE_LOCK(ump) rw_wlock((ump)->um_softdep->sd_fslock)
#define FREE_LOCK(ump) rw_wunlock((ump)->um_softdep->sd_fslock)
#define LOCK_OWNED(ump) rw_assert((ump)->um_softdep->sd_fslock, \
#define LOCK_PTR(ump) (&(ump)->um_softdep->sd_fslock)
#define TRY_ACQUIRE_LOCK(ump) rw_try_wlock(&(ump)->um_softdep->sd_fslock)
#define ACQUIRE_LOCK(ump) rw_wlock(&(ump)->um_softdep->sd_fslock)
#define FREE_LOCK(ump) rw_wunlock(&(ump)->um_softdep->sd_fslock)
#define LOCK_OWNED(ump) rw_assert(&(ump)->um_softdep->sd_fslock, \
RA_WLOCKED)
#define BUF_AREC(bp) lockallowrecurse(&(bp)->b_lock)
@ -1178,7 +1181,7 @@ workitem_free(item, type)
KASSERT(ump->softdep_curdeps[item->wk_type] > 0,
("workitem_free: %s: softdep_curdeps[%s] going negative",
ump->um_fs->fs_fsmnt, TYPENAME(item->wk_type)));
dep_current[item->wk_type]--;
atomic_subtract_long(&dep_current[item->wk_type], 1);
ump->softdep_curdeps[item->wk_type] -= 1;
free(item, DtoM(type));
}
@ -1196,11 +1199,13 @@ workitem_alloc(item, type, mp)
item->wk_state = 0;
ump = VFSTOUFS(mp);
ACQUIRE_LOCK(ump);
ACQUIRE_GBLLOCK(&lk);
dep_current[type]++;
if (dep_current[type] > dep_highuse[type])
dep_highuse[type] = dep_current[type];
dep_total[type]++;
FREE_GBLLOCK(&lk);
ACQUIRE_LOCK(ump);
ump->softdep_curdeps[type] += 1;
ump->softdep_deps++;
ump->softdep_accdeps++;
@ -1224,11 +1229,13 @@ workitem_reassign(item, newtype)
KASSERT(dep_current[item->wk_type] > 0,
("workitem_reassign: %s: dep_current[%s] going negative",
VFSTOUFS(item->wk_mp)->um_fs->fs_fsmnt, TYPENAME(item->wk_type)));
dep_current[item->wk_type]--;
ACQUIRE_GBLLOCK(&lk);
dep_current[newtype]++;
dep_current[item->wk_type]--;
if (dep_current[newtype] > dep_highuse[newtype])
dep_highuse[newtype] = dep_current[newtype];
dep_total[newtype]++;
FREE_GBLLOCK(&lk);
item->wk_type = newtype;
}
@ -1236,13 +1243,10 @@ workitem_reassign(item, newtype)
* Workitem queue management
*/
static int max_softdeps; /* maximum number of structs before slowdown */
static int maxindirdeps = 50; /* max number of indirdeps before slowdown */
static int tickdelay = 2; /* number of ticks to pause during slowdown */
static int proc_waiting; /* tracks whether we have a timeout posted */
static int *stat_countp; /* statistic to count in proc_waiting timeout */
static struct callout softdep_callout;
static struct mount *req_pending;
#define ALLCLEAN ((struct mount *)-1)
static int req_clear_inodedeps; /* syncer process flush some inodedeps */
static int req_clear_remove; /* syncer process flush some freeblks */
static int softdep_flushcache = 0; /* Should we do BIO_FLUSH? */
@ -1250,7 +1254,7 @@ static int softdep_flushcache = 0; /* Should we do BIO_FLUSH? */
/*
* runtime statistics
*/
static int stat_softdep_mounts; /* number of softdep mounted filesystems */
static int stat_flush_threads; /* number of softdep flushing threads */
static int stat_worklist_push; /* number of worklist cleanups */
static int stat_blk_limit_push; /* number of times block limit neared */
static int stat_ino_limit_push; /* number of times inode limit neared */
@ -1281,10 +1285,8 @@ SYSCTL_INT(_debug_softdep, OID_AUTO, max_softdeps, CTLFLAG_RW,
&max_softdeps, 0, "");
SYSCTL_INT(_debug_softdep, OID_AUTO, tickdelay, CTLFLAG_RW,
&tickdelay, 0, "");
SYSCTL_INT(_debug_softdep, OID_AUTO, maxindirdeps, CTLFLAG_RW,
&maxindirdeps, 0, "");
SYSCTL_INT(_debug_softdep, OID_AUTO, softdep_mounts, CTLFLAG_RD,
&stat_softdep_mounts, 0, "");
SYSCTL_INT(_debug_softdep, OID_AUTO, flush_threads, CTLFLAG_RD,
&stat_flush_threads, 0, "");
SYSCTL_INT(_debug_softdep, OID_AUTO, worklist_push, CTLFLAG_RW,
&stat_worklist_push, 0,"");
SYSCTL_INT(_debug_softdep, OID_AUTO, blk_limit_push, CTLFLAG_RW,
@ -1344,53 +1346,67 @@ SYSCTL_DECL(_vfs_ffs);
static int compute_summary_at_mount = 0;
SYSCTL_INT(_vfs_ffs, OID_AUTO, compute_summary_at_mount, CTLFLAG_RW,
&compute_summary_at_mount, 0, "Recompute summary at mount");
static struct proc *softdepproc;
static struct kproc_desc softdep_kp = {
"softdepflush",
softdep_flush,
&softdepproc
};
SYSINIT(sdproc, SI_SUB_KTHREAD_UPDATE, SI_ORDER_ANY, kproc_start,
&softdep_kp);
static int print_threads = 0;
SYSCTL_INT(_debug_softdep, OID_AUTO, print_threads, CTLFLAG_RW,
&print_threads, 0, "Notify flusher thread start/stop");
/* List of all filesystems mounted with soft updates */
static TAILQ_HEAD(, mount_softdeps) softdepmounts;
/*
* This function cleans the worklist for a filesystem.
* Each filesystem running with soft dependencies gets its own
* thread to run in this function. The thread is started up in
* softdep_mount and shutdown in softdep_unmount. They show up
* as part of the kernel "bufdaemon" process whose process
* entry is available in bufdaemonproc.
*/
static int searchfailed;
extern struct proc *bufdaemonproc;
static void
softdep_flush(void)
softdep_flush(addr)
void *addr;
{
struct mount *nmp;
struct mount *mp;
struct ufsmount *ump;
struct thread *td;
int remaining;
int progress;
struct ufsmount *ump;
td = curthread;
td->td_pflags |= TDP_NORUNNINGBUF;
mp = (struct mount *)addr;
ump = VFSTOUFS(mp);
atomic_add_int(&stat_flush_threads, 1);
if (print_threads) {
if (stat_flush_threads == 1)
printf("Running %s at pid %d\n", bufdaemonproc->p_comm,
bufdaemonproc->p_pid);
printf("Start thread %s\n", td->td_name);
}
for (;;) {
kproc_suspend_check(softdepproc);
remaining = progress = 0;
mtx_lock(&mountlist_mtx);
for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) {
nmp = TAILQ_NEXT(mp, mnt_list);
if (MOUNTEDSOFTDEP(mp) == 0)
continue;
if (vfs_busy(mp, MBF_NOWAIT | MBF_MNTLSTLOCK))
continue;
ump = VFSTOUFS(mp);
progress += softdep_process_worklist(mp, 0);
remaining += ump->softdep_on_worklist;
mtx_lock(&mountlist_mtx);
nmp = TAILQ_NEXT(mp, mnt_list);
vfs_unbusy(mp);
}
mtx_unlock(&mountlist_mtx);
if (remaining && progress)
while (softdep_process_worklist(mp, 0) > 0 ||
(MOUNTEDSUJ(mp) &&
VFSTOUFS(mp)->softdep_jblocks->jb_suspended))
kthread_suspend_check();
ACQUIRE_LOCK(ump);
if ((ump->softdep_flags & FLUSH_CLEANUP) == 0)
msleep(&ump->softdep_flushtd, LOCK_PTR(ump), PVM,
"sdflush", hz / 2);
ump->softdep_flags &= ~FLUSH_CLEANUP;
/*
* Check to see if we are done and need to exit.
*/
if ((ump->softdep_flags & FLUSH_EXIT) == 0) {
FREE_LOCK(ump);
continue;
rw_wlock(&lk);
if (req_pending == NULL)
msleep(&req_pending, &lk, PVM, "sdflush", hz);
req_pending = NULL;
rw_wunlock(&lk);
}
ump->softdep_flags &= ~FLUSH_EXIT;
FREE_LOCK(ump);
wakeup(&ump->softdep_flags);
if (print_threads)
printf("Stop thread %s: searchfailed %d, did cleanups %d\n", td->td_name, searchfailed, ump->um_softdep->sd_cleanups);
atomic_subtract_int(&stat_flush_threads, 1);
kthread_exit();
panic("kthread_exit failed\n");
}
}
@ -1398,19 +1414,70 @@ static void
worklist_speedup(mp)
struct mount *mp;
{
rw_assert(&lk, RA_WLOCKED);
if (req_pending == 0) {
req_pending = mp;
wakeup(&req_pending);
struct ufsmount *ump;
ump = VFSTOUFS(mp);
LOCK_OWNED(ump);
if ((ump->softdep_flags & (FLUSH_CLEANUP | FLUSH_EXIT)) == 0) {
ump->softdep_flags |= FLUSH_CLEANUP;
if (ump->softdep_flushtd->td_wchan == &ump->softdep_flushtd)
wakeup(&ump->softdep_flushtd);
}
}
static int
softdep_speedup(void)
softdep_speedup(ump)
struct ufsmount *ump;
{
struct ufsmount *altump;
struct mount_softdeps *sdp;
worklist_speedup(ALLCLEAN);
LOCK_OWNED(ump);
worklist_speedup(ump->um_mountp);
bd_speedup();
/*
* If we have global shortages, then we need other
* filesystems to help with the cleanup. Here we wakeup a
* flusher thread for a filesystem that is over its fair
* share of resources.
*/
if (req_clear_inodedeps || req_clear_remove) {
ACQUIRE_GBLLOCK(&lk);
TAILQ_FOREACH(sdp, &softdepmounts, sd_next) {
if ((altump = sdp->sd_ump) == ump)
continue;
if (((req_clear_inodedeps &&
altump->softdep_curdeps[D_INODEDEP] >
max_softdeps / stat_flush_threads) ||
(req_clear_remove &&
altump->softdep_curdeps[D_DIRREM] >
(max_softdeps / 2) / stat_flush_threads)) &&
TRY_ACQUIRE_LOCK(altump))
break;
}
if (sdp == NULL) {
searchfailed++;
FREE_GBLLOCK(&lk);
} else {
/*
* Move to the end of the list so we pick a
* different one on out next try.
*/
TAILQ_REMOVE(&softdepmounts, sdp, sd_next);
TAILQ_INSERT_TAIL(&softdepmounts, sdp, sd_next);
FREE_GBLLOCK(&lk);
if ((altump->softdep_flags &
(FLUSH_CLEANUP | FLUSH_EXIT)) == 0) {
altump->softdep_flags |= FLUSH_CLEANUP;
altump->um_softdep->sd_cleanups++;
if (altump->softdep_flushtd->td_wchan ==
&altump->softdep_flushtd) {
wakeup(&altump->softdep_flushtd);
}
}
FREE_LOCK(altump);
}
}
return (speedup_syncer());
}
@ -2126,9 +2193,14 @@ inodedep_lookup(mp, inum, flags, inodedeppp)
if ((flags & DEPALLOC) == 0)
return (0);
/*
* If we are over our limit, try to improve the situation.
* If the system is over its limit and our filesystem is
* responsible for more than our share of that usage and
* we are not in a rush, request some inodedep cleanup.
*/
if (dep_current[D_INODEDEP] > max_softdeps && (flags & NODELAY) == 0)
while (dep_current[D_INODEDEP] > max_softdeps &&
(flags & NODELAY) == 0 &&
ump->softdep_curdeps[D_INODEDEP] >
max_softdeps / stat_flush_threads)
request_cleanup(mp, FLUSH_INODES);
FREE_LOCK(ump);
inodedep = malloc(sizeof(struct inodedep),
@ -2320,6 +2392,7 @@ void
softdep_initialize()
{
TAILQ_INIT(&softdepmounts);
max_softdeps = desiredvnodes * 4;
/* initialise bioops hack */
@ -2378,7 +2451,9 @@ softdep_mount(devvp, mp, fs, cred)
ump = VFSTOUFS(mp);
ump->um_softdep = sdp;
MNT_IUNLOCK(mp);
LOCK_PTR(ump) = &lk;
rw_init(LOCK_PTR(ump), "Per-Filesystem Softdep Lock");
TAILQ_INSERT_TAIL(&softdepmounts, sdp, sd_next);
sdp->sd_ump = ump;
LIST_INIT(&ump->softdep_workitem_pending);
LIST_INIT(&ump->softdep_journal_pending);
TAILQ_INIT(&ump->softdep_unlinked);
@ -2409,7 +2484,12 @@ softdep_mount(devvp, mp, fs, cred)
softdep_unmount(mp);
return (error);
}
atomic_add_int(&stat_softdep_mounts, 1);
/*
* Start our flushing thread in the bufdaemon process.
*/
kproc_kthread_add(&softdep_flush, mp, &bufdaemonproc,
&ump->softdep_flushtd, 0, 0, "softdepflush", "%s worker",
mp->mnt_stat.f_mntonname);
/*
* When doing soft updates, the counters in the
* superblock may have gotten out of sync. Recomputation
@ -2465,7 +2545,24 @@ softdep_unmount(mp)
MNT_IUNLOCK(mp);
journal_unmount(ump);
}
atomic_subtract_int(&stat_softdep_mounts, 1);
/*
* Shut down our flushing thread. Check for NULL is if
* softdep_mount errors out before the thread has been created.
*/
if (ump->softdep_flushtd != NULL) {
ACQUIRE_LOCK(ump);
ump->softdep_flags |= FLUSH_EXIT;
wakeup(&ump->softdep_flushtd);
msleep(&ump->softdep_flags, LOCK_PTR(ump), PVM | PDROP,
"sdwait", 0);
KASSERT((ump->softdep_flags & FLUSH_EXIT) == 0,
("Thread shutdown failed"));
}
/*
* Free up our resources.
*/
rw_destroy(LOCK_PTR(ump));
TAILQ_REMOVE(&softdepmounts, ump->um_softdep, sd_next);
hashdestroy(ump->pagedep_hashtbl, M_PAGEDEP, ump->pagedep_hash_size);
hashdestroy(ump->inodedep_hashtbl, M_INODEDEP, ump->inodedep_hash_size);
hashdestroy(ump->newblk_hashtbl, M_NEWBLK, ump->newblk_hash_size);
@ -2788,7 +2885,7 @@ journal_space(ump, thresh)
*/
limit = (max_softdeps / 10) * 9;
if (dep_current[D_INODEDEP] > limit &&
ump->softdep_curdeps[D_INODEDEP] > limit / stat_softdep_mounts)
ump->softdep_curdeps[D_INODEDEP] > limit / stat_flush_threads)
return (0);
if (thresh)
thresh = jblocks->jb_min;
@ -2813,7 +2910,7 @@ journal_suspend(ump)
if ((mp->mnt_kern_flag & MNTK_SUSPEND) == 0) {
stat_journal_min++;
mp->mnt_kern_flag |= MNTK_SUSPEND;
mp->mnt_susp_owner = FIRST_THREAD_IN_PROC(softdepproc);
mp->mnt_susp_owner = ump->softdep_flushtd;
}
jblocks->jb_suspended = 1;
MNT_IUNLOCK(mp);
@ -2888,7 +2985,7 @@ softdep_prealloc(vp, waitok)
process_removes(vp);
process_truncates(vp);
if (journal_space(ump, 0) == 0) {
softdep_speedup();
softdep_speedup(ump);
if (journal_space(ump, 1) == 0)
journal_suspend(ump);
}
@ -2932,10 +3029,10 @@ softdep_prelink(dvp, vp)
}
process_removes(dvp);
process_truncates(dvp);
softdep_speedup();
softdep_speedup(ump);
process_worklist_item(UFSTOVFS(ump), 2, LK_NOWAIT);
if (journal_space(ump, 0) == 0) {
softdep_speedup();
softdep_speedup(ump);
if (journal_space(ump, 1) == 0)
journal_suspend(ump);
}
@ -3257,7 +3354,7 @@ softdep_process_journal(mp, needwk, flags)
if (flags != MNT_WAIT)
break;
printf("softdep: Out of journal space!\n");
softdep_speedup();
softdep_speedup(ump);
msleep(jblocks, LOCK_PTR(ump), PRIBIO, "jblocks", hz);
}
FREE_LOCK(ump);
@ -3970,7 +4067,7 @@ free_freedep(freedep)
/*
* Allocate a new freework structure that may be a level in an indirect
* when parent is not NULL or a top level block when it is. The top level
* freework structures are allocated without the soft updates lock held
* freework structures are allocated without the per-filesystem lock held
* and before the freeblks is visible outside of softdep_setup_freeblocks().
*/
static struct freework *
@ -4039,7 +4136,7 @@ cancel_jfreeblk(freeblks, blkno)
/*
* Allocate a new jfreeblk to journal top level block pointer when truncating
* a file. The caller must add this to the worklist when the soft updates
* a file. The caller must add this to the worklist when the per-filesystem
* lock is held.
*/
static struct jfreeblk *
@ -7419,7 +7516,7 @@ softdep_freefile(pvp, ino, mode)
clear_unlinked_inodedep(inodedep);
/*
* Re-acquire inodedep as we've dropped the
* soft updates lock in clear_unlinked_inodedep().
* per-filesystem lock in clear_unlinked_inodedep().
*/
inodedep_lookup(pvp->v_mount, ino, 0, &inodedep);
}
@ -7965,10 +8062,8 @@ indir_trunc(freework, dbn, lbn)
* If we're goingaway, free the indirdep. Otherwise it will
* linger until the write completes.
*/
if (goingaway) {
if (goingaway)
free_indirdep(indirdep);
ump->softdep_numindirdeps -= 1;
}
}
FREE_LOCK(ump);
/* Initialize pointers depending on block size. */
@ -8140,7 +8235,7 @@ cancel_allocindir(aip, bp, freeblks, trunc)
* Create the mkdir dependencies for . and .. in a new directory. Link them
* in to a newdirblk so any subsequent additions are tracked properly. The
* caller is responsible for adding the mkdir1 dependency to the journal
* and updating id_mkdiradd. This function returns with the soft updates
* and updating id_mkdiradd. This function returns with the per-filesystem
* lock held.
*/
static struct mkdir *
@ -8958,12 +9053,16 @@ newdirrem(bp, dp, ip, isrmdir, prevdirremp)
panic("newdirrem: whiteout");
dvp = ITOV(dp);
/*
* If we are over our limit, try to improve the situation.
* If the system is over its limit and our filesystem is
* responsible for more than our share of that usage and
* we are not a snapshot, request some inodedep cleanup.
* Limiting the number of dirrem structures will also limit
* the number of freefile and freeblks structures.
*/
ACQUIRE_LOCK(ip->i_ump);
if (!IS_SNAPSHOT(ip) && dep_current[D_DIRREM] > max_softdeps / 2)
while (!IS_SNAPSHOT(ip) && dep_current[D_DIRREM] > max_softdeps / 2 &&
ip->i_ump->softdep_curdeps[D_DIRREM] >
(max_softdeps / 2) / stat_flush_threads)
(void) request_cleanup(ITOV(dp)->v_mount, FLUSH_BLOCKS);
FREE_LOCK(ip->i_ump);
dirrem = malloc(sizeof(struct dirrem),
@ -9914,7 +10013,7 @@ initiate_write_filepage(pagedep, bp)
* Wait for all journal remove dependencies to hit the disk.
* We can not allow any potentially conflicting directory adds
* to be visible before removes and rollback is too difficult.
* The soft updates lock may be dropped and re-acquired, however
* The per-filesystem lock may be dropped and re-acquired, however
* we hold the buf locked so the dependency can not go away.
*/
LIST_FOREACH(dirrem, &pagedep->pd_dirremhd, dm_next)
@ -10378,7 +10477,6 @@ cancel_indirdep(indirdep, bp, freeblks)
LIST_REMOVE(indirdep, ir_next);
}
indirdep->ir_state |= GOINGAWAY;
VFSTOUFS(indirdep->ir_list.wk_mp)->softdep_numindirdeps += 1;
/*
* Pass in bp for blocks still have journal writes
* pending so we can cancel them on their own.
@ -10805,7 +10903,7 @@ softdep_disk_write_complete(bp)
ACQUIRE_LOCK(ump);
while ((wk = LIST_FIRST(&bp->b_dep)) != NULL) {
WORKLIST_REMOVE(wk);
dep_write[wk->wk_type]++;
atomic_add_long(&dep_write[wk->wk_type], 1);
if (wk == owk)
panic("duplicate worklist: %p\n", wk);
owk = wk;
@ -11488,7 +11586,7 @@ diradd_inode_written(dap, inodedep)
/*
* Returns true if the bmsafemap will have rollbacks when written. Must only
* be called with the soft updates lock and the buf lock on the cg held.
* be called with the per-filesystem lock and the buf lock on the cg held.
*/
static int
bmsafemap_backgroundwrite(bmsafemap, bp)
@ -12912,18 +13010,42 @@ softdep_slowdown(vp)
if (journal_space(ump, 0) == 0)
jlow = 1;
}
/*
* If the system is under its limits and our filesystem is
* not responsible for more than our share of the usage and
* we are not low on journal space, then no need to slow down.
*/
max_softdeps_hard = max_softdeps * 11 / 10;
if (dep_current[D_DIRREM] < max_softdeps_hard / 2 &&
dep_current[D_INODEDEP] < max_softdeps_hard &&
VFSTOUFS(vp->v_mount)->softdep_numindirdeps < maxindirdeps &&
dep_current[D_FREEBLKS] < max_softdeps_hard && jlow == 0) {
dep_current[D_INDIRDEP] < max_softdeps_hard / 1000 &&
dep_current[D_FREEBLKS] < max_softdeps_hard && jlow == 0 &&
ump->softdep_curdeps[D_DIRREM] <
(max_softdeps_hard / 2) / stat_flush_threads &&
ump->softdep_curdeps[D_INODEDEP] <
max_softdeps_hard / stat_flush_threads &&
ump->softdep_curdeps[D_INDIRDEP] <
(max_softdeps_hard / 1000) / stat_flush_threads &&
ump->softdep_curdeps[D_FREEBLKS] <
max_softdeps_hard / stat_flush_threads) {
FREE_LOCK(ump);
return (0);
}
if (VFSTOUFS(vp->v_mount)->softdep_numindirdeps >= maxindirdeps || jlow)
softdep_speedup();
/*
* If the journal is low or our filesystem is over its limit
* then speedup the cleanup.
*/
if (ump->softdep_curdeps[D_INDIRDEP] <
(max_softdeps_hard / 1000) / stat_flush_threads || jlow)
softdep_speedup(ump);
stat_sync_limit_hit += 1;
FREE_LOCK(ump);
/*
* We only slow down the rate at which new dependencies are
* generated if we are not using journaling. With journaling,
* the cleanup should always be sufficient to keep things
* under control.
*/
if (DOINGSUJ(vp))
return (0);
return (1);
@ -12981,13 +13103,12 @@ softdep_request_cleanup(fs, vp, cred, resource)
return (0);
}
/*
* If we are in need of resources, consider pausing for
* tickdelay to give ourselves some breathing room.
* If we are in need of resources, start by cleaning up
* any block removals associated with our inode.
*/
ACQUIRE_LOCK(ump);
process_removes(vp);
process_truncates(vp);
request_cleanup(UFSTOVFS(ump), resource);
FREE_LOCK(ump);
/*
* Now clean up at least as many resources as we will need.
@ -13120,7 +13241,7 @@ request_cleanup(mp, resource)
* Next, we attempt to speed up the syncer process. If that
* is successful, then we allow the process to continue.
*/
if (softdep_speedup() &&
if (softdep_speedup(ump) &&
resource != FLUSH_BLOCKS_WAIT &&
resource != FLUSH_INODES_WAIT)
return(0);
@ -13138,15 +13259,19 @@ request_cleanup(mp, resource)
case FLUSH_INODES:
case FLUSH_INODES_WAIT:
ACQUIRE_GBLLOCK(&lk);
stat_ino_limit_push += 1;
req_clear_inodedeps += 1;
FREE_GBLLOCK(&lk);
stat_countp = &stat_ino_limit_hit;
break;
case FLUSH_BLOCKS:
case FLUSH_BLOCKS_WAIT:
ACQUIRE_GBLLOCK(&lk);
stat_blk_limit_push += 1;
req_clear_remove += 1;
FREE_GBLLOCK(&lk);
stat_countp = &stat_blk_limit_hit;
break;
@ -13157,6 +13282,8 @@ request_cleanup(mp, resource)
* Hopefully the syncer daemon will catch up and awaken us.
* We wait at most tickdelay before proceeding in any case.
*/
ACQUIRE_GBLLOCK(&lk);
FREE_LOCK(ump);
proc_waiting += 1;
if (callout_pending(&softdep_callout) == FALSE)
callout_reset(&softdep_callout, tickdelay > 2 ? tickdelay : 2,
@ -13164,6 +13291,8 @@ request_cleanup(mp, resource)
msleep((caddr_t)&proc_waiting, &lk, PPAUSE, "softupdate", 0);
proc_waiting -= 1;
FREE_GBLLOCK(&lk);
ACQUIRE_LOCK(ump);
return (1);
}
@ -13177,16 +13306,13 @@ pause_timer(arg)
void *arg;
{
rw_assert(&lk, RA_WLOCKED);
GBLLOCK_OWNED(&lk);
/*
* The callout_ API has acquired mtx and will hold it around this
* function call.
*/
*stat_countp += 1;
wakeup_one(&proc_waiting);
if (proc_waiting > 0)
callout_reset(&softdep_callout, tickdelay > 2 ? tickdelay : 2,
pause_timer, 0);
*stat_countp += proc_waiting;
wakeup(&proc_waiting);
}
/*
@ -13197,7 +13323,6 @@ check_clear_deps(mp)
struct mount *mp;
{
rw_assert(&lk, RA_WLOCKED);
/*
* If we are suspended, it may be because of our using
* too many inodedeps, so help clear them out.
@ -13207,16 +13332,22 @@ check_clear_deps(mp)
/*
* General requests for cleanup of backed up dependencies
*/
ACQUIRE_GBLLOCK(&lk);
if (req_clear_inodedeps) {
req_clear_inodedeps -= 1;
FREE_GBLLOCK(&lk);
clear_inodedeps(mp);
wakeup_one(&proc_waiting);
ACQUIRE_GBLLOCK(&lk);
wakeup(&proc_waiting);
}
if (req_clear_remove) {
req_clear_remove -= 1;
FREE_GBLLOCK(&lk);
clear_remove(mp);
wakeup_one(&proc_waiting);
ACQUIRE_GBLLOCK(&lk);
wakeup(&proc_waiting);
}
FREE_GBLLOCK(&lk);
}
/*

View File

@ -1025,7 +1025,7 @@ TAILQ_HEAD(indir_hashhead, freework);
* Allocated at mount and freed at unmount.
*/
struct mount_softdeps {
struct rwlock *sd_fslock; /* softdep lock */
struct rwlock sd_fslock; /* softdep lock */
struct workhead sd_workitem_pending; /* softdep work queue */
struct worklist *sd_worklist_tail; /* Tail pointer for above */
struct workhead sd_journal_pending; /* journal work queue */
@ -1046,14 +1046,23 @@ struct mount_softdeps {
u_long sd_bmhashsize; /* bmsafemap hash table size-1*/
struct indir_hashhead *sd_indirhash; /* indir hash table */
u_long sd_indirhashsize; /* indir hash table size-1 */
long sd_numindirdeps; /* outstanding indirdeps */
int sd_on_journal; /* Items on the journal list */
int sd_on_worklist; /* Items on the worklist */
int sd_deps; /* Total dependency count */
int sd_accdeps; /* accumulated dep count */
int sd_req; /* Wakeup when deps hits 0. */
int sd_flags; /* comm with flushing thread */
int sd_cleanups; /* Calls to cleanup */
struct thread *sd_flushtd; /* thread handling flushing */
TAILQ_ENTRY(mount_softdeps) sd_next; /* List of softdep filesystem */
struct ufsmount *sd_ump; /* our ufsmount structure */
u_long sd_curdeps[D_LAST + 1]; /* count of current deps */
};
/*
* Flags for communicating with the syncer thread.
*/
#define FLUSH_EXIT 0x0001 /* time to exit */
#define FLUSH_CLEANUP 0x0002 /* need to clear out softdep structures */
/*
* Keep the old names from when these were in the ufsmount structure.
*/
@ -1077,10 +1086,11 @@ struct mount_softdeps {
#define bmsafemap_hash_size um_softdep->sd_bmhashsize
#define indir_hashtbl um_softdep->sd_indirhash
#define indir_hash_size um_softdep->sd_indirhashsize
#define softdep_numindirdeps um_softdep->sd_numindirdeps
#define softdep_on_journal um_softdep->sd_on_journal
#define softdep_on_worklist um_softdep->sd_on_worklist
#define softdep_deps um_softdep->sd_deps
#define softdep_accdeps um_softdep->sd_accdeps
#define softdep_req um_softdep->sd_req
#define softdep_flags um_softdep->sd_flags
#define softdep_flushtd um_softdep->sd_flushtd
#define softdep_curdeps um_softdep->sd_curdeps