STATIC	struct dirrem *newdirrem __P((struct buf *, struct inode *, struct inode *, int, struct dirrem **)); STATIC	void free_diradd __P((struct diradd *)); STATIC	void free_allocindir __P((struct allocindir *, struct inodedep *)); STATIC	int indir_trunc __P((struct inode *, ufs_daddr_t, int, ufs_lbn_t, long *)); STATIC	void deallocate_dependencies __P((struct buf *, struct inodedep *)); STATIC	void free_allocdirect __P((struct allocdirectlst *, struct allocdirect *, int)); STATIC	int check_inode_unwritten __P((struct inodedep *)); STATIC	int free_inodedep __P((struct inodedep *)); STATIC	void handle_workitem_freeblocks __P((struct freeblks *)); STATIC	void merge_inode_lists __P((struct inodedep *)); STATIC	void setup_allocindir_phase2 __P((struct buf *, struct inode *, struct allocindir *)); STATIC	struct allocindir *newallocindir __P((struct inode *, int, ufs_daddr_t, ufs_daddr_t)); STATIC	void handle_workitem_freefrag __P((struct freefrag *)); STATIC	struct freefrag *newfreefrag __P((struct inode *, ufs_daddr_t, long)); STATIC	void allocdirect_merge __P((struct allocdirectlst *, struct allocdirect *, struct allocdirect *)); STATIC	struct bmsafemap *bmsafemap_lookup __P((struct buf *)); STATIC	int newblk_lookup __P((struct fs *, ufs_daddr_t, int, struct newblk **)); STATIC	int inodedep_lookup __P((struct fs *, ino_t, int, struct inodedep **)); STATIC	int pagedep_lookup __P((struct inode *, ufs_lbn_t, int, struct pagedep **)); STATIC	void pause_timer __P((void *)); STATIC	int request_cleanup __P((int, int)); STATIC	int process_worklist_item __P((struct mount *, int)); STATIC	void add_to_worklist __P((struct worklist *)); STATIC struct buf *softdep_setup_pagecache __P((struct inode *, ufs_lbn_t, long)); STATIC void softdep_collect_pagecache __P((struct inode *)); STATIC void softdep_free_pagecache __P((struct inode *)); STATIC struct vnode *softdep_lookupvp(struct fs *, ino_t); STATIC struct buf *softdep_lookup_pcbp __P((struct vnode *, ufs_lbn_t)); void softdep_pageiodone __P((struct buf *));   void softdep_disk_io_initiation __P((struct buf *)); void softdep_disk_write_complete __P((struct buf *)); void softdep_deallocate_dependencies __P((struct buf *)); void softdep_move_dependencies __P((struct buf *, struct buf *)); int softdep_count_dependencies __P((struct buf *bp, int, int));  struct bio_ops bioops = { softdep_disk_io_initiation,		 softdep_disk_write_complete,		 softdep_deallocate_dependencies,	 softdep_move_dependencies,		 softdep_count_dependencies,		 softdep_pageiodone,			 };   #ifndef  DEBUG STATIC struct lockit { int	lkt_spl; } lk = { 0 }; #define ACQUIRE_LOCK(lk)		(lk)->lkt_spl = splbio() #define FREE_LOCK(lk)			splx((lk)->lkt_spl) #define ACQUIRE_LOCK_INTERLOCKED(lk,s)	(lk)->lkt_spl = (s) #define FREE_LOCK_INTERLOCKED(lk)	((lk)->lkt_spl)  #else  STATIC struct lockit { int	lkt_spl; pid_t	lkt_held; int     lkt_line; } lk = { 0, -1 }; STATIC int lockcnt;  STATIC	void acquire_lock __P((struct lockit *, int)); STATIC	void free_lock __P((struct lockit *, int)); STATIC	void acquire_lock_interlocked __P((struct lockit *, int, int)); STATIC	int free_lock_interlocked __P((struct lockit *, int));  #define ACQUIRE_LOCK(lk)		acquire_lock(lk, __LINE__) #define FREE_LOCK(lk)			free_lock(lk, __LINE__) #define ACQUIRE_LOCK_INTERLOCKED(lk,s)	acquire_lock_interlocked(lk, (s), __LINE__) #define FREE_LOCK_INTERLOCKED(lk)	free_lock_interlocked(lk, __LINE__)  STATIC void acquire_lock(lk, line) struct lockit *lk; int line; { pid_t holder; int original_line;  if (lk->lkt_held != -1) { holder = lk->lkt_held; original_line = lk->lkt_line; FREE_LOCK(lk); if (holder == CURPROC->p_pid) panic("softdep_lock: locking against myself, acquired at line %d, relocked at line %d", original_line, line); else panic("softdep_lock: lock held by %d, acquired at line %d, relocked at line %d", holder, original_line, line); } lk->lkt_spl = splbio(); lk->lkt_held = CURPROC->p_pid; lk->lkt_line = line; lockcnt++; }  STATIC void free_lock(lk, line) struct lockit *lk; int line; {  if (lk->lkt_held == -1) panic("softdep_unlock: lock not held at line %d", line); lk->lkt_held = -1; splx(lk->lkt_spl); }  STATIC void acquire_lock_interlocked(lk, s, line) struct lockit *lk; int s; int line; { pid_t holder; int original_line;  if (lk->lkt_held != -1) { holder = lk->lkt_held; original_line = lk->lkt_line; FREE_LOCK_INTERLOCKED(lk); if (holder == CURPROC->p_pid) panic("softdep_lock: locking against myself, acquired at line %d, relocked at line %d", original_line, line); else panic("softdep_lock: lock held by %d, acquired at line %d, relocked at line %d", holder, original_line, line); } lk->lkt_held = CURPROC->p_pid; lk->lkt_line = line; lk->lkt_spl = s; lockcnt++; }  STATIC int free_lock_interlocked(lk, line) struct lockit *lk; int line; {  if (lk->lkt_held == -1) panic("softdep_unlock_interlocked: lock not held at line %d", line); lk->lkt_held = -1;  return (lk->lkt_spl); } #endif    struct sema { int	value; pid_t	holder; char	*name; int	prio; int	timo; }; STATIC	void sema_init __P((struct sema *, char *, int, int)); STATIC	int sema_get __P((struct sema *, struct lockit *)); STATIC	void sema_release __P((struct sema *));  STATIC void sema_init(semap, name, prio, timo) struct sema *semap; char *name; int prio, timo; {  semap->holder = -1; semap->value = 0; semap->name = name; semap->prio = prio; semap->timo = timo; }  STATIC int sema_get(semap, interlock) struct sema *semap; struct lockit *interlock; { int s;  if (semap->value++ > 0) { if (interlock != NULL) s = FREE_LOCK_INTERLOCKED(interlock); tsleep((caddr_t)semap, semap->prio, semap->name, semap->timo); if (interlock != NULL) { ACQUIRE_LOCK_INTERLOCKED(interlock, s); FREE_LOCK(interlock); } return (0); } semap->holder = CURPROC->p_pid; if (interlock != NULL) FREE_LOCK(interlock); return (1); }  STATIC void sema_release(semap) struct sema *semap; {  if (semap->value <= 0 || semap->holder != CURPROC->p_pid) { #ifdef DEBUG if (lk.lkt_held != -1) FREE_LOCK(&lk); #endif panic("sema_release: not held"); } if (--semap->value > 0) { semap->value = 0; wakeup(semap); } semap->holder = -1; }   #ifndef  DEBUG #define WORKLIST_INSERT(head, item) do {	\ (item)->wk_state |= ONWORKLIST;		\ LIST_INSERT_HEAD(head, item, wk_list);	\ } while (0) #define WORKLIST_REMOVE(item) do {		\ (item)->wk_state &= ~ONWORKLIST;	\ LIST_REMOVE(item, wk_list);		\ } while (0) #define WORKITEM_FREE(item, type) FREE(item, DtoM(type))  #else  STATIC	void worklist_insert __P((struct workhead *, struct worklist *)); STATIC	void worklist_remove __P((struct worklist *)); STATIC	void workitem_free __P((struct worklist *, int));  #define WORKLIST_INSERT(head, item) worklist_insert(head, item) #define WORKLIST_REMOVE(item) worklist_remove(item) #define WORKITEM_FREE(item, type) workitem_free((struct worklist *)item, type)  STATIC void worklist_insert(head, item) struct workhead *head; struct worklist *item; {  if (lk.lkt_held == -1) panic("worklist_insert: lock not held"); if (item->wk_state & ONWORKLIST) { FREE_LOCK(&lk); panic("worklist_insert: already on list"); } item->wk_state |= ONWORKLIST; LIST_INSERT_HEAD(head, item, wk_list); }  STATIC void worklist_remove(item) struct worklist *item; {  if (lk.lkt_held == -1) panic("worklist_remove: lock not held"); if ((item->wk_state & ONWORKLIST) == 0) { FREE_LOCK(&lk); panic("worklist_remove: not on list"); } item->wk_state &= ~ONWORKLIST; LIST_REMOVE(item, wk_list); }  STATIC void workitem_free(item, type) struct worklist *item; int type; {  if (item->wk_state & ONWORKLIST) { if (lk.lkt_held != -1) FREE_LOCK(&lk); panic("workitem_free: still on list"); } if (item->wk_type != type) { if (lk.lkt_held != -1) FREE_LOCK(&lk); panic("workitem_free: type mismatch"); } FREE(item, DtoM(type)); } #endif    STATIC struct workhead softdep_workitem_pending; STATIC int num_on_worklist;	 STATIC int softdep_worklist_busy;  STATIC int softdep_worklist_req;  STATIC int max_softdeps;	 STATIC int tickdelay = 2;	 STATIC int proc_waiting;	 STATIC int *stat_countp;	 STATIC struct timeout proc_waiting_timeout; STATIC struct proc *filesys_syncer;  STATIC int req_clear_inodedeps;	 #define FLUSH_INODES	1 STATIC int req_clear_remove;	 #define FLUSH_REMOVE	2  STATIC int stat_worklist_push;	 STATIC int stat_blk_limit_push;	 STATIC int stat_ino_limit_push;	 STATIC int stat_blk_limit_hit;	 STATIC int stat_ino_limit_hit;	 STATIC int stat_sync_limit_hit;	 STATIC int stat_indir_blk_ptrs;	 STATIC int stat_inode_bitmap;	 STATIC int stat_direct_blk_ptrs; STATIC int stat_dir_entry;	 #ifdef DEBUG #include <uvm/uvm_extern.h> #include <sys/sysctl.h> struct ctldebug debug20 = { "max_softdeps", &max_softdeps }; struct ctldebug debug21 = { "tickdelay", &tickdelay }; struct ctldebug debug22 = { "worklist_push", &stat_worklist_push }; struct ctldebug debug23 = { "blk_limit_push", &stat_blk_limit_push }; struct ctldebug debug24 = { "ino_limit_push", &stat_ino_limit_push }; struct ctldebug debug25 = { "blk_limit_hit", &stat_blk_limit_hit }; struct ctldebug debug26 = { "ino_limit_hit", &stat_ino_limit_hit }; struct ctldebug debug27 = { "sync_limit_hit", &stat_sync_limit_hit }; struct ctldebug debug28 = { "indir_blk_ptrs", &stat_indir_blk_ptrs }; struct ctldebug debug29 = { "inode_bitmap", &stat_inode_bitmap }; struct ctldebug debug30 = { "direct_blk_ptrs", &stat_direct_blk_ptrs }; struct ctldebug debug31 = { "dir_entry", &stat_dir_entry }; #endif    STATIC void add_to_worklist(wk) struct worklist *wk; { static struct worklist *worklist_tail;  if (wk->wk_state & ONWORKLIST) { #ifdef DEBUG if (lk.lkt_held != -1) FREE_LOCK(&lk); #endif panic("add_to_worklist: already on list"); } wk->wk_state |= ONWORKLIST; if (LIST_FIRST(&softdep_workitem_pending) == NULL) LIST_INSERT_HEAD(&softdep_workitem_pending, wk, wk_list); else LIST_INSERT_AFTER(worklist_tail, wk, wk_list); worklist_tail = wk; num_on_worklist += 1; }   int softdep_process_worklist(matchmnt) struct mount *matchmnt; { struct proc *p = CURPROC; int matchcnt, loopcount; struct timeval starttime;   filesys_syncer = syncerproc; matchcnt = 0;   if (matchmnt == NULL) { if (softdep_worklist_busy < 0) return(-1); softdep_worklist_busy += 1; }   if (req_clear_inodedeps) { clear_inodedeps(p); req_clear_inodedeps -= 1; wakeup_one(&proc_waiting); } if (req_clear_remove) { clear_remove(p); req_clear_remove -= 1; wakeup_one(&proc_waiting); } loopcount = 1; starttime = time; while (num_on_worklist > 0) { matchcnt += process_worklist_item(matchmnt, 0);   if (softdep_worklist_req && matchmnt == NULL) { matchcnt = -1; break; }   if (req_clear_inodedeps) { clear_inodedeps(p); req_clear_inodedeps -= 1; wakeup_one(&proc_waiting); } if (req_clear_remove) { clear_remove(p); req_clear_remove -= 1; wakeup_one(&proc_waiting); }  #if 0 if (loopcount++ % 128 == 0) bwillwrite(); #endif  { struct timeval diff;  timersub(&time, &starttime, &diff); if (diff.tv_sec != 0 && matchmnt == NULL) { matchcnt = -1; break; } } } if (matchmnt == NULL) { softdep_worklist_busy -= 1; if (softdep_worklist_req && softdep_worklist_busy == 0) wakeup(&softdep_worklist_req); } return (matchcnt); }   STATIC int process_worklist_item(matchmnt, flags) struct mount *matchmnt; int flags; { struct worklist *wk; struct dirrem *dirrem; struct mount *mp; struct vnode *vp; int matchcnt = 0;  ACQUIRE_LOCK(&lk);  LIST_FOREACH(wk, &softdep_workitem_pending, wk_list) { if ((flags & LK_NOWAIT) == 0 || wk->wk_type != D_DIRREM) break; dirrem = WK_DIRREM(wk); vp = ufs_ihashlookup(VFSTOUFS(dirrem->dm_mnt)->um_dev, dirrem->dm_oldinum); if (vp == NULL || !VOP_ISLOCKED(vp)) break; } if (wk == 0) { FREE_LOCK(&lk); return (0); } WORKLIST_REMOVE(wk); num_on_worklist -= 1; FREE_LOCK(&lk); switch (wk->wk_type) {  case D_DIRREM:  mp = WK_DIRREM(wk)->dm_mnt; #if 0 if (vn_write_suspend_wait(NULL, mp, V_NOWAIT)) panic("%s: dirrem on suspended filesystem", "process_worklist_item"); #endif if (mp == matchmnt) matchcnt += 1; handle_workitem_remove(WK_DIRREM(wk)); break;  case D_FREEBLKS:  mp = WK_FREEBLKS(wk)->fb_mnt; #if 0 if (vn_write_suspend_wait(NULL, mp, V_NOWAIT)) panic("%s: freeblks on suspended filesystem", "process_worklist_item"); #endif if (mp == matchmnt) matchcnt += 1; handle_workitem_freeblocks(WK_FREEBLKS(wk)); break;  case D_FREEFRAG:  mp = WK_FREEFRAG(wk)->ff_mnt; #if 0 if (vn_write_suspend_wait(NULL, mp, V_NOWAIT)) panic("%s: freefrag on suspended filesystem", "process_worklist_item"); #endif if (mp == matchmnt) matchcnt += 1; handle_workitem_freefrag(WK_FREEFRAG(wk)); break;  case D_FREEFILE:  mp = WK_FREEFILE(wk)->fx_mnt; #if 0 if (vn_write_suspend_wait(NULL, mp, V_NOWAIT)) panic("%s: freefile on suspended filesystem", "process_worklist_item"); #endif if (mp == matchmnt) matchcnt += 1; handle_workitem_freefile(WK_FREEFILE(wk)); break;  default: panic("%s_process_worklist: Unknown type %s", "softdep", TYPENAME(wk->wk_type));  } return (matchcnt); }   void softdep_move_dependencies(oldbp, newbp) struct buf *oldbp; struct buf *newbp; { struct worklist *wk, *wktail;  if (LIST_FIRST(&newbp->b_dep) != NULL) panic("softdep_move_dependencies: need merge code"); wktail = 0; ACQUIRE_LOCK(&lk); while ((wk = LIST_FIRST(&oldbp->b_dep)) != NULL) { LIST_REMOVE(wk, wk_list); if (wktail == 0) LIST_INSERT_HEAD(&newbp->b_dep, wk, wk_list); else LIST_INSERT_AFTER(wktail, wk, wk_list); wktail = wk; } FREE_LOCK(&lk); }   int softdep_flushworklist(oldmnt, countp, p) struct mount *oldmnt; int *countp; struct proc *p; { struct vnode *devvp; int count, error = 0;   while (softdep_worklist_busy) { softdep_worklist_req += 1; tsleep(&softdep_worklist_req, PRIBIO, "softflush", 0); softdep_worklist_req -= 1; } softdep_worklist_busy = -1;  *countp = 0; devvp = VFSTOUFS(oldmnt)->um_devvp; while ((count = softdep_process_worklist(oldmnt)) > 0) { *countp += count; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_FSYNC(devvp, p->p_ucred, MNT_WAIT, p); VOP_UNLOCK(devvp, 0, p); if (error) break; } softdep_worklist_busy = 0; if (softdep_worklist_req) wakeup(&softdep_worklist_req); return (error); }   int softdep_flushfiles(oldmnt, flags, p) struct mount *oldmnt; int flags; struct proc *p; { int error, count, loopcnt;   for (loopcnt = 10; loopcnt > 0; loopcnt--) {  if ((error = ffs_flushfiles(oldmnt, flags, p)) != 0) break; if ((error = softdep_flushworklist(oldmnt, &count, p)) != 0 || count == 0) break; }  if (loopcnt == 0) { if (oldmnt->mnt_flag & MNT_UNMOUNT) panic("softdep_flushfiles: looping"); error = EBUSY; } return (error); }   #define DEPALLOC	0x0001	 #define NODELAY         0x0002     LIST_HEAD(pagedep_hashhead, pagedep) *pagedep_hashtbl; u_long	pagedep_hash;		 #define	PAGEDEP_HASH(mp, inum, lbn) \ (&pagedep_hashtbl[((((register_t)(mp)) >> 13) + (inum) + (lbn)) & \ pagedep_hash]) STATIC struct sema pagedep_in_progress;   STATIC int pagedep_lookup(ip, lbn, flags, pagedeppp) struct inode *ip; ufs_lbn_t lbn; int flags; struct pagedep **pagedeppp; { struct pagedep *pagedep; struct pagedep_hashhead *pagedephd; struct mount *mp; int i;  #ifdef DEBUG if (lk.lkt_held == -1) panic("pagedep_lookup: lock not held"); #endif mp = ITOV(ip)->v_mount; pagedephd = PAGEDEP_HASH(mp, ip->i_number, lbn); top: LIST_FOREACH(pagedep, pagedephd, pd_hash) if (ip->i_number == pagedep->pd_ino && lbn == pagedep->pd_lbn && mp == pagedep->pd_mnt) break; if (pagedep) { *pagedeppp = pagedep; return (1); } if ((flags & DEPALLOC) == 0) { *pagedeppp = NULL; return (0); } if (sema_get(&pagedep_in_progress, &lk) == 0) { ACQUIRE_LOCK(&lk); goto top; } MALLOC(pagedep, struct pagedep *, sizeof(struct pagedep), M_PAGEDEP, M_SOFTDEP_FLAGS); bzero(pagedep, sizeof(struct pagedep)); pagedep->pd_list.wk_type = D_PAGEDEP; pagedep->pd_mnt = mp; pagedep->pd_ino = ip->i_number; pagedep->pd_lbn = lbn; LIST_INIT(&pagedep->pd_dirremhd); LIST_INIT(&pagedep->pd_pendinghd); for (i = 0; i < DAHASHSZ; i++) LIST_INIT(&pagedep->pd_diraddhd[i]); ACQUIRE_LOCK(&lk); LIST_INSERT_HEAD(pagedephd, pagedep, pd_hash); sema_release(&pagedep_in_progress); *pagedeppp = pagedep; return (0); }   LIST_HEAD(inodedep_hashhead, inodedep) *inodedep_hashtbl; STATIC u_long	inodedep_hash;	 STATIC long	num_inodedep;	 #define	INODEDEP_HASH(fs, inum) \ (&inodedep_hashtbl[((((register_t)(fs)) >> 13) + (inum)) & inodedep_hash]) STATIC struct sema inodedep_in_progress;   STATIC int inodedep_lookup(fs, inum, flags, inodedeppp) struct fs *fs; ino_t inum; int flags; struct inodedep **inodedeppp; { struct inodedep *inodedep; struct inodedep_hashhead *inodedephd; int firsttry;  #ifdef DEBUG if (lk.lkt_held == -1) panic("inodedep_lookup: lock not held"); #endif firsttry = 1; inodedephd = INODEDEP_HASH(fs, inum); top: LIST_FOREACH(inodedep, inodedephd, id_hash) if (inum == inodedep->id_ino && fs == inodedep->id_fs) break; if (inodedep) { *inodedeppp = inodedep; return (1); } if ((flags & DEPALLOC) == 0) { *inodedeppp = NULL; return (0); }  if (num_inodedep > max_softdeps && firsttry && (flags & NODELAY) == 0 && request_cleanup(FLUSH_INODES, 1)) { firsttry = 0; goto top; } if (sema_get(&inodedep_in_progress, &lk) == 0) { ACQUIRE_LOCK(&lk); goto top; } num_inodedep += 1; MALLOC(inodedep, struct inodedep *, sizeof(struct inodedep), M_INODEDEP, M_SOFTDEP_FLAGS); inodedep->id_list.wk_type = D_INODEDEP; inodedep->id_fs = fs; inodedep->id_ino = inum; inodedep->id_state = ALLCOMPLETE; inodedep->id_nlinkdelta = 0; inodedep->id_savedino = NULL; inodedep->id_savedsize = -1; inodedep->id_buf = NULL; LIST_INIT(&inodedep->id_pendinghd); LIST_INIT(&inodedep->id_inowait); LIST_INIT(&inodedep->id_bufwait); TAILQ_INIT(&inodedep->id_inoupdt); TAILQ_INIT(&inodedep->id_newinoupdt); ACQUIRE_LOCK(&lk); LIST_INSERT_HEAD(inodedephd, inodedep, id_hash); sema_release(&inodedep_in_progress); *inodedeppp = inodedep; return (0); }   LIST_HEAD(newblk_hashhead, newblk) *newblk_hashtbl; u_long	newblk_hash;		 #define	NEWBLK_HASH(fs, inum) \ (&newblk_hashtbl[((((register_t)(fs)) >> 13) + (inum)) & newblk_hash]) STATIC struct sema newblk_in_progress;   STATIC int newblk_lookup(fs, newblkno, flags, newblkpp) struct fs *fs; ufs_daddr_t newblkno; int flags; struct newblk **newblkpp; { struct newblk *newblk; struct newblk_hashhead *newblkhd;  newblkhd = NEWBLK_HASH(fs, newblkno); top: LIST_FOREACH(newblk, newblkhd, nb_hash) if (newblkno == newblk->nb_newblkno && fs == newblk->nb_fs) break; if (newblk) { *newblkpp = newblk; return (1); } if ((flags & DEPALLOC) == 0) { *newblkpp = NULL; return (0); } if (sema_get(&newblk_in_progress, 0) == 0) goto top; MALLOC(newblk, struct newblk *, sizeof(struct newblk), M_NEWBLK, M_SOFTDEP_FLAGS); newblk->nb_state = 0; newblk->nb_fs = fs; newblk->nb_newblkno = newblkno; LIST_INSERT_HEAD(newblkhd, newblk, nb_hash); sema_release(&newblk_in_progress); *newblkpp = newblk; return (0); }   void softdep_initialize() { int i;  LIST_INIT(&mkdirlisthd); LIST_INIT(&softdep_workitem_pending); #ifdef KMEMSTATS max_softdeps = min (desiredvnodes * 8, kmemstats[M_INODEDEP].ks_limit / (2 * sizeof(struct inodedep))); #else max_softdeps = desiredvnodes * 4; #endif pagedep_hashtbl = hashinit(desiredvnodes / 5, M_PAGEDEP, M_WAITOK, &pagedep_hash); sema_init(&pagedep_in_progress, "pagedep", PRIBIO, 0); inodedep_hashtbl = hashinit(desiredvnodes, M_INODEDEP, M_WAITOK, &inodedep_hash); sema_init(&inodedep_in_progress, "inodedep", PRIBIO, 0); newblk_hashtbl = hashinit(64, M_NEWBLK, M_WAITOK, &newblk_hash); sema_init(&newblk_in_progress, "newblk", PRIBIO, 0); timeout_set(&proc_waiting_timeout, pause_timer, 0); pool_init(&sdpcpool, sizeof(struct buf), 0, 0, 0, "sdpcpool", 0, pool_page_alloc_nointr, pool_page_free_nointr, M_TEMP); for (i = 0; i < PCBPHASHSIZE; i++) { LIST_INIT(&pcbphashhead[i]); } }   int softdep_mount(devvp, mp, fs, cred) struct vnode *devvp; struct mount *mp; struct fs *fs; struct ucred *cred; { struct csum cstotal; struct cg *cgp; struct buf *bp; int error, cyl;   if ((fs->fs_flags & FS_UNCLEAN) == 0) return (0); bzero(&cstotal, sizeof cstotal); for (cyl = 0; cyl < fs->fs_ncg; cyl++) { if ((error = bread(devvp, fsbtodb(fs, cgtod(fs, cyl)), fs->fs_cgsize, cred, &bp)) != 0) { brelse(bp); return (error); } cgp = (struct cg *)bp->b_data; cstotal.cs_nffree += cgp->cg_cs.cs_nffree; cstotal.cs_nbfree += cgp->cg_cs.cs_nbfree; cstotal.cs_nifree += cgp->cg_cs.cs_nifree; cstotal.cs_ndir += cgp->cg_cs.cs_ndir; fs->fs_cs(fs, cyl) = cgp->cg_cs; brelse(bp); } #ifdef DEBUG if (bcmp(&cstotal, &fs->fs_cstotal, sizeof cstotal)) printf("ffs_mountfs: superblock updated for soft updates\n"); #endif bcopy(&cstotal, &fs->fs_cstotal, sizeof cstotal); return (0); }     void softdep_setup_inomapdep(bp, ip, newinum) struct buf *bp;		 struct inode *ip;	 ino_t newinum;		 { struct inodedep *inodedep; struct bmsafemap *bmsafemap;   ACQUIRE_LOCK(&lk); if (inodedep_lookup(ip->i_fs, newinum, DEPALLOC | NODELAY, &inodedep) != 0) { FREE_LOCK(&lk); panic("softdep_setup_inomapdep: found inode"); } inodedep->id_buf = bp; inodedep->id_state &= ~DEPCOMPLETE; bmsafemap = bmsafemap_lookup(bp); LIST_INSERT_HEAD(&bmsafemap->sm_inodedephd, inodedep, id_deps); FREE_LOCK(&lk); }   void softdep_setup_blkmapdep(bp, fs, newblkno) struct buf *bp;		 struct fs *fs;		 ufs_daddr_t newblkno;	 { struct newblk *newblk; struct bmsafemap *bmsafemap;   if (newblk_lookup(fs, newblkno, DEPALLOC, &newblk) != 0) panic("softdep_setup_blkmapdep: found block"); ACQUIRE_LOCK(&lk); newblk->nb_bmsafemap = bmsafemap = bmsafemap_lookup(bp); LIST_INSERT_HEAD(&bmsafemap->sm_newblkhd, newblk, nb_deps); FREE_LOCK(&lk); }   STATIC struct bmsafemap * bmsafemap_lookup(bp) struct buf *bp; { struct bmsafemap *bmsafemap; struct worklist *wk;  #ifdef DEBUG if (lk.lkt_held == -1) panic("bmsafemap_lookup: lock not held"); #endif LIST_FOREACH(wk, &bp->b_dep, wk_list) if (wk->wk_type == D_BMSAFEMAP) return (WK_BMSAFEMAP(wk)); FREE_LOCK(&lk); MALLOC(bmsafemap, struct bmsafemap *, sizeof(struct bmsafemap), M_BMSAFEMAP, M_SOFTDEP_FLAGS); bmsafemap->sm_list.wk_type = D_BMSAFEMAP; bmsafemap->sm_list.wk_state = 0; bmsafemap->sm_buf = bp; LIST_INIT(&bmsafemap->sm_allocdirecthd); LIST_INIT(&bmsafemap->sm_allocindirhd); LIST_INIT(&bmsafemap->sm_inodedephd); LIST_INIT(&bmsafemap->sm_newblkhd); ACQUIRE_LOCK(&lk); WORKLIST_INSERT(&bp->b_dep, &bmsafemap->sm_list); return (bmsafemap); }   void softdep_setup_allocdirect(ip, lbn, newblkno, oldblkno, newsize, oldsize, bp) struct inode *ip;	 ufs_lbn_t lbn;		 ufs_daddr_t newblkno;	 ufs_daddr_t oldblkno;	 long newsize;		 long oldsize;		 struct buf *bp;		 { struct allocdirect *adp, *oldadp; struct allocdirectlst *adphead; struct bmsafemap *bmsafemap; struct inodedep *inodedep; struct pagedep *pagedep; struct newblk *newblk;  MALLOC(adp, struct allocdirect *, sizeof(struct allocdirect), M_ALLOCDIRECT, M_SOFTDEP_FLAGS); bzero(adp, sizeof(struct allocdirect)); adp->ad_list.wk_type = D_ALLOCDIRECT; adp->ad_lbn = lbn; adp->ad_newblkno = newblkno; adp->ad_oldblkno = oldblkno; adp->ad_newsize = newsize; adp->ad_oldsize = oldsize; adp->ad_state = ATTACHED; if (newblkno == oldblkno) adp->ad_freefrag = NULL; else adp->ad_freefrag = newfreefrag(ip, oldblkno, oldsize);  if (newblk_lookup(ip->i_fs, newblkno, 0, &newblk) == 0) panic("softdep_setup_allocdirect: lost block");  ACQUIRE_LOCK(&lk); inodedep_lookup(ip->i_fs, ip->i_number, DEPALLOC | NODELAY, &inodedep); adp->ad_inodedep = inodedep;  if (newblk->nb_state == DEPCOMPLETE) { adp->ad_state |= DEPCOMPLETE; adp->ad_buf = NULL; } else { bmsafemap = newblk->nb_bmsafemap; adp->ad_buf = bmsafemap->sm_buf; LIST_REMOVE(newblk, nb_deps); LIST_INSERT_HEAD(&bmsafemap->sm_allocdirecthd, adp, ad_deps); } LIST_REMOVE(newblk, nb_hash); FREE(newblk, M_NEWBLK);   if (bp == NULL) { bp = softdep_setup_pagecache(ip, lbn, newsize); } WORKLIST_INSERT(&bp->b_dep, &adp->ad_list); if (lbn >= NDADDR) {  if (oldblkno != 0) { FREE_LOCK(&lk); panic("softdep_setup_allocdirect: non-zero indir"); } } else {  if ((ip->i_ffs_mode & IFMT) == IFDIR && pagedep_lookup(ip, lbn, DEPALLOC, &pagedep) == 0) WORKLIST_INSERT(&bp->b_dep, &pagedep->pd_list); }  adphead = &inodedep->id_newinoupdt; oldadp = TAILQ_LAST(adphead, allocdirectlst); if (oldadp == NULL || oldadp->ad_lbn <= lbn) {  TAILQ_INSERT_TAIL(adphead, adp, ad_next); if (oldadp != NULL && oldadp->ad_lbn == lbn) allocdirect_merge(adphead, adp, oldadp); FREE_LOCK(&lk); return; } TAILQ_FOREACH(oldadp, adphead, ad_next) { if (oldadp->ad_lbn >= lbn) break; } if (oldadp == NULL) { FREE_LOCK(&lk); panic("softdep_setup_allocdirect: lost entry"); }  TAILQ_INSERT_BEFORE(oldadp, adp, ad_next); if (oldadp->ad_lbn == lbn) allocdirect_merge(adphead, adp, oldadp); FREE_LOCK(&lk); }   STATIC void allocdirect_merge(adphead, newadp, oldadp) struct allocdirectlst *adphead;	 struct allocdirect *newadp;	 struct allocdirect *oldadp;	 { struct freefrag *freefrag;  #ifdef DEBUG if (lk.lkt_held == -1) panic("allocdirect_merge: lock not held"); #endif if (newadp->ad_oldblkno != oldadp->ad_newblkno || newadp->ad_oldsize != oldadp->ad_newsize || newadp->ad_lbn >= NDADDR) { FREE_LOCK(&lk); panic("allocdirect_check: old %d != new %d || lbn %ld >= %d", newadp->ad_oldblkno, oldadp->ad_newblkno, newadp->ad_lbn, NDADDR); } newadp->ad_oldblkno = oldadp->ad_oldblkno; newadp->ad_oldsize = oldadp->ad_oldsize;  if (oldadp->ad_freefrag != NULL || oldadp->ad_oldblkno == 0) { freefrag = newadp->ad_freefrag; newadp->ad_freefrag = oldadp->ad_freefrag; oldadp->ad_freefrag = freefrag; } free_allocdirect(adphead, oldadp, 0); }   STATIC struct freefrag * newfreefrag(ip, blkno, size) struct inode *ip; ufs_daddr_t blkno; long size; { struct freefrag *freefrag; struct fs *fs;  if (blkno == 0) return (NULL); fs = ip->i_fs; if (fragnum(fs, blkno) + numfrags(fs, size) > fs->fs_frag) panic("newfreefrag: frag size"); MALLOC(freefrag, struct freefrag *, sizeof(struct freefrag), M_FREEFRAG, M_SOFTDEP_FLAGS); freefrag->ff_list.wk_type = D_FREEFRAG; freefrag->ff_state = ip->i_ffs_uid & ~ONWORKLIST;  freefrag->ff_inum = ip->i_number; freefrag->ff_mnt = ITOV(ip)->v_mount; freefrag->ff_devvp = ip->i_devvp; freefrag->ff_blkno = blkno; freefrag->ff_fragsize = size; return (freefrag); }   STATIC void handle_workitem_freefrag(freefrag) struct freefrag *freefrag; { struct inode tip;  tip.i_vnode = NULL; tip.i_fs = VFSTOUFS(freefrag->ff_mnt)->um_fs; tip.i_devvp = freefrag->ff_devvp; tip.i_dev = freefrag->ff_devvp->v_rdev; tip.i_number = freefrag->ff_inum; tip.i_ffs_uid = freefrag->ff_state & ~ONWORKLIST;  ffs_blkfree(&tip, freefrag->ff_blkno, freefrag->ff_fragsize); FREE(freefrag, M_FREEFRAG); }     STATIC struct allocindir * newallocindir(ip, ptrno, newblkno, oldblkno) struct inode *ip;	 int ptrno;		 ufs_daddr_t newblkno;	 ufs_daddr_t oldblkno;	 { struct allocindir *aip;  MALLOC(aip, struct allocindir *, sizeof(struct allocindir), M_ALLOCINDIR, M_SOFTDEP_FLAGS); bzero(aip,sizeof(struct allocindir)); aip->ai_list.wk_type = D_ALLOCINDIR; aip->ai_state = ATTACHED; aip->ai_offset = ptrno; aip->ai_newblkno = newblkno; aip->ai_oldblkno = oldblkno; aip->ai_freefrag = newfreefrag(ip, oldblkno, ip->i_fs->fs_bsize); return (aip); }   void softdep_setup_allocindir_page(ip, lbn, bp, ptrno, newblkno, oldblkno, nbp) struct inode *ip;	 ufs_lbn_t lbn;		 struct buf *bp;		 int ptrno;		 ufs_daddr_t newblkno;	 ufs_daddr_t oldblkno;	 struct buf *nbp;	 { struct allocindir *aip; struct pagedep *pagedep;  aip = newallocindir(ip, ptrno, newblkno, oldblkno); ACQUIRE_LOCK(&lk);  if ((ip->i_ffs_mode & IFMT) == IFDIR && pagedep_lookup(ip, lbn, DEPALLOC, &pagedep) == 0) WORKLIST_INSERT(&nbp->b_dep, &pagedep->pd_list); if (nbp == NULL) { nbp = softdep_setup_pagecache(ip, lbn, ip->i_fs->fs_bsize); } WORKLIST_INSERT(&nbp->b_dep, &aip->ai_list); FREE_LOCK(&lk); setup_allocindir_phase2(bp, ip, aip); }   void softdep_setup_allocindir_meta(nbp, ip, bp, ptrno, newblkno) struct buf *nbp;	 struct inode *ip;	 struct buf *bp;		 int ptrno;		 ufs_daddr_t newblkno;	 { struct allocindir *aip;  aip = newallocindir(ip, ptrno, newblkno, 0); ACQUIRE_LOCK(&lk); WORKLIST_INSERT(&nbp->b_dep, &aip->ai_list); FREE_LOCK(&lk); setup_allocindir_phase2(bp, ip, aip); }   STATIC void setup_allocindir_phase2(bp, ip, aip) struct buf *bp;		 struct inode *ip;	 struct allocindir *aip;	 { struct worklist *wk; struct indirdep *indirdep, *newindirdep; struct bmsafemap *bmsafemap; struct allocindir *oldaip; struct freefrag *freefrag; struct newblk *newblk;  if (bp->b_lblkno >= 0) panic("setup_allocindir_phase2: not indir blk"); for (indirdep = NULL, newindirdep = NULL; ; ) { ACQUIRE_LOCK(&lk); LIST_FOREACH(wk, &bp->b_dep, wk_list) { if (wk->wk_type != D_INDIRDEP) continue; indirdep = WK_INDIRDEP(wk); break; } if (indirdep == NULL && newindirdep) { indirdep = newindirdep; WORKLIST_INSERT(&bp->b_dep, &indirdep->ir_list); newindirdep = NULL; } FREE_LOCK(&lk); if (indirdep) { if (newblk_lookup(ip->i_fs, aip->ai_newblkno, 0, &newblk) == 0) panic("setup_allocindir: lost block"); ACQUIRE_LOCK(&lk); if (newblk->nb_state == DEPCOMPLETE) { aip->ai_state |= DEPCOMPLETE; aip->ai_buf = NULL; } else { bmsafemap = newblk->nb_bmsafemap; aip->ai_buf = bmsafemap->sm_buf; LIST_REMOVE(newblk, nb_deps); LIST_INSERT_HEAD(&bmsafemap->sm_allocindirhd, aip, ai_deps); } LIST_REMOVE(newblk, nb_hash); FREE(newblk, M_NEWBLK); aip->ai_indirdep = indirdep;  if (aip->ai_oldblkno == 0) oldaip = NULL; else  LIST_FOREACH(oldaip, &indirdep->ir_deplisthd, ai_next) if (oldaip->ai_offset == aip->ai_offset) break; freefrag = NULL; if (oldaip != NULL) { if (oldaip->ai_newblkno != aip->ai_oldblkno) { FREE_LOCK(&lk); panic("setup_allocindir_phase2: blkno"); } aip->ai_oldblkno = oldaip->ai_oldblkno; freefrag = aip->ai_freefrag; aip->ai_freefrag = oldaip->ai_freefrag; oldaip->ai_freefrag = NULL; free_allocindir(oldaip, NULL); } LIST_INSERT_HEAD(&indirdep->ir_deplisthd, aip, ai_next); ((ufs_daddr_t *)indirdep->ir_savebp->b_data) [aip->ai_offset] = aip->ai_oldblkno; FREE_LOCK(&lk); if (freefrag != NULL) handle_workitem_freefrag(freefrag); } if (newindirdep) { if (indirdep->ir_savebp != NULL) brelse(newindirdep->ir_savebp); WORKITEM_FREE((caddr_t)newindirdep, D_INDIRDEP); } if (indirdep) break; MALLOC(newindirdep, struct indirdep *, sizeof(struct indirdep), M_INDIRDEP, M_SOFTDEP_FLAGS); newindirdep->ir_list.wk_type = D_INDIRDEP; newindirdep->ir_state = ATTACHED; LIST_INIT(&newindirdep->ir_deplisthd); LIST_INIT(&newindirdep->ir_donehd); if (bp->b_blkno == bp->b_lblkno) { VOP_BMAP(bp->b_vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL); } newindirdep->ir_savebp = getblk(ip->i_devvp, bp->b_blkno, bp->b_bcount, 0, 0); #if 0 BUF_KERNPROC(newindirdep->ir_savebp); #endif bcopy(bp->b_data, newindirdep->ir_savebp->b_data, bp->b_bcount); } }   void softdep_setup_freeblocks(ip, length) struct inode *ip;	 off_t length;		 { struct freeblks *freeblks; struct inodedep *inodedep; struct allocdirect *adp; struct vnode *vp; struct buf *bp; struct fs *fs; int i, delay, error;  fs = ip->i_fs; vp = ITOV(ip); if (length != 0) panic("softdep_setup_freeblocks: non-zero length"); MALLOC(freeblks, struct freeblks *, sizeof(struct freeblks), M_FREEBLKS, M_SOFTDEP_FLAGS); bzero(freeblks, sizeof(struct freeblks)); freeblks->fb_list.wk_type = D_FREEBLKS; freeblks->fb_uid = ip->i_ffs_uid; freeblks->fb_previousinum = ip->i_number; freeblks->fb_devvp = ip->i_devvp; freeblks->fb_mnt = ITOV(ip)->v_mount; freeblks->fb_oldsize = ip->i_ffs_size; freeblks->fb_newsize = length; freeblks->fb_chkcnt = ip->i_ffs_blocks; for (i = 0; i < NDADDR; i++) { freeblks->fb_dblks[i] = ip->i_ffs_db[i]; ip->i_ffs_db[i] = 0; } for (i = 0; i < NIADDR; i++) { freeblks->fb_iblks[i] = ip->i_ffs_ib[i]; ip->i_ffs_ib[i] = 0; } ip->i_ffs_blocks = 0; ip->i_ffs_size = 0;  if ((error = bread(ip->i_devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), (int)fs->fs_bsize, NOCRED, &bp)) != 0) softdep_error("softdep_setup_freeblocks", error); *((struct dinode *)bp->b_data + ino_to_fsbo(fs, ip->i_number)) = ip->i_din.ffs_din;  ACQUIRE_LOCK(&lk); (void) inodedep_lookup(fs, ip->i_number, DEPALLOC, &inodedep); if ((inodedep->id_state & IOSTARTED) != 0) { FREE_LOCK(&lk); panic("softdep_setup_freeblocks: inode busy"); }  delay = (inodedep->id_state & DEPCOMPLETE); if (delay) WORKLIST_INSERT(&inodedep->id_bufwait, &freeblks->fb_list);  softdep_collect_pagecache(ip); merge_inode_lists(inodedep); while ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != 0) free_allocdirect(&inodedep->id_inoupdt, adp, delay); FREE_LOCK(&lk); bdwrite(bp);  ACQUIRE_LOCK(&lk); drain_output(vp, 1); while (getdirtybuf(&LIST_FIRST(&vp->v_dirtyblkhd), MNT_WAIT)) { bp = LIST_FIRST(&vp->v_dirtyblkhd); (void) inodedep_lookup(fs, ip->i_number, 0, &inodedep); deallocate_dependencies(bp, inodedep); bp->b_flags |= B_INVAL | B_NOCACHE; FREE_LOCK(&lk); brelse(bp); ACQUIRE_LOCK(&lk); } softdep_free_pagecache(ip); if (inodedep_lookup(fs, ip->i_number, 0, &inodedep) != 0) (void) free_inodedep(inodedep); FREE_LOCK(&lk);  if (!delay) handle_workitem_freeblocks(freeblks); }   STATIC void deallocate_dependencies(bp, inodedep) struct buf *bp; struct inodedep *inodedep; { struct worklist *wk; struct indirdep *indirdep; struct allocindir *aip; struct pagedep *pagedep; struct dirrem *dirrem; struct diradd *dap; int i;  while ((wk = LIST_FIRST(&bp->b_dep)) != NULL) { switch (wk->wk_type) {  case D_INDIRDEP: indirdep = WK_INDIRDEP(wk);  if (indirdep->ir_state & GOINGAWAY) { FREE_LOCK(&lk); panic("deallocate_dependencies: already gone"); } indirdep->ir_state |= GOINGAWAY; while ((aip = LIST_FIRST(&indirdep->ir_deplisthd)) != 0) free_allocindir(aip, inodedep); if (bp->b_lblkno >= 0 || bp->b_blkno != indirdep->ir_savebp->b_lblkno) { FREE_LOCK(&lk); panic("deallocate_dependencies: not indir"); } bcopy(bp->b_data, indirdep->ir_savebp->b_data, bp->b_bcount); WORKLIST_REMOVE(wk); WORKLIST_INSERT(&indirdep->ir_savebp->b_dep, wk); continue;  case D_PAGEDEP: pagedep = WK_PAGEDEP(wk);  for (i = 0; i < DAHASHSZ; i++) while ((dap = LIST_FIRST(&pagedep->pd_diraddhd[i]))) free_diradd(dap); while ((dap = LIST_FIRST(&pagedep->pd_pendinghd)) != 0) free_diradd(dap);  LIST_FOREACH(dirrem, &pagedep->pd_dirremhd, dm_next) { LIST_REMOVE(dirrem, dm_next); dirrem->dm_dirinum = pagedep->pd_ino; if (inodedep == NULL || (inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) add_to_worklist(&dirrem->dm_list); else WORKLIST_INSERT(&inodedep->id_bufwait, &dirrem->dm_list); } WORKLIST_REMOVE(&pagedep->pd_list); LIST_REMOVE(pagedep, pd_hash); WORKITEM_FREE(pagedep, D_PAGEDEP); continue;  case D_ALLOCINDIR: free_allocindir(WK_ALLOCINDIR(wk), inodedep); continue;  case D_ALLOCDIRECT: case D_INODEDEP: FREE_LOCK(&lk); panic("deallocate_dependencies: Unexpected type %s", TYPENAME(wk->wk_type));   default: FREE_LOCK(&lk); panic("deallocate_dependencies: Unknown type %s", TYPENAME(wk->wk_type));  } } }   STATIC void free_allocdirect(adphead, adp, delay) struct allocdirectlst *adphead; struct allocdirect *adp; int delay; {  #ifdef DEBUG if (lk.lkt_held == -1) panic("free_allocdirect: lock not held"); #endif if ((adp->ad_state & DEPCOMPLETE) == 0) LIST_REMOVE(adp, ad_deps); TAILQ_REMOVE(adphead, adp, ad_next); if ((adp->ad_state & COMPLETE) == 0) WORKLIST_REMOVE(&adp->ad_list); if (adp->ad_freefrag != NULL) { if (delay) WORKLIST_INSERT(&adp->ad_inodedep->id_bufwait, &adp->ad_freefrag->ff_list); else add_to_worklist(&adp->ad_freefrag->ff_list); } WORKITEM_FREE(adp, D_ALLOCDIRECT); }   void softdep_freefile(pvp, ino, mode) struct vnode *pvp; ino_t ino; int mode; { struct inode *ip = VTOI(pvp); struct inodedep *inodedep; struct freefile *freefile;   MALLOC(freefile, struct freefile *, sizeof(struct freefile), M_FREEFILE, M_SOFTDEP_FLAGS); freefile->fx_list.wk_type = D_FREEFILE; freefile->fx_list.wk_state = 0; freefile->fx_mode = mode; freefile->fx_oldinum = ino; freefile->fx_devvp = ip->i_devvp; freefile->fx_mnt = ITOV(ip)->v_mount;   ACQUIRE_LOCK(&lk); if (inodedep_lookup(ip->i_fs, ino, 0, &inodedep) == 0 || check_inode_unwritten(inodedep)) { FREE_LOCK(&lk); handle_workitem_freefile(freefile); return; } WORKLIST_INSERT(&inodedep->id_inowait, &freefile->fx_list); FREE_LOCK(&lk); }   STATIC int check_inode_unwritten(inodedep) struct inodedep *inodedep; {  if ((inodedep->id_state & DEPCOMPLETE) != 0 || LIST_FIRST(&inodedep->id_pendinghd) != NULL || LIST_FIRST(&inodedep->id_bufwait) != NULL || LIST_FIRST(&inodedep->id_inowait) != NULL || TAILQ_FIRST(&inodedep->id_inoupdt) != NULL || TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL || inodedep->id_nlinkdelta != 0) return (0); inodedep->id_state |= ALLCOMPLETE; LIST_REMOVE(inodedep, id_deps); inodedep->id_buf = NULL; if (inodedep->id_state & ONWORKLIST) WORKLIST_REMOVE(&inodedep->id_list); if (inodedep->id_savedino != NULL) { FREE(inodedep->id_savedino, M_INODEDEP); inodedep->id_savedino = NULL; } if (free_inodedep(inodedep) == 0) { FREE_LOCK(&lk); panic("check_inode_unwritten: busy inode"); } return (1); }   STATIC int free_inodedep(inodedep) struct inodedep *inodedep; {  if ((inodedep->id_state & ONWORKLIST) != 0 || (inodedep->id_state & ALLCOMPLETE) != ALLCOMPLETE || LIST_FIRST(&inodedep->id_pendinghd) != NULL || LIST_FIRST(&inodedep->id_bufwait) != NULL || LIST_FIRST(&inodedep->id_inowait) != NULL || TAILQ_FIRST(&inodedep->id_inoupdt) != NULL || TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL || inodedep->id_nlinkdelta != 0 || inodedep->id_savedino != NULL) return (0); LIST_REMOVE(inodedep, id_hash); WORKITEM_FREE(inodedep, D_INODEDEP); num_inodedep -= 1; return (1); }   STATIC void handle_workitem_freeblocks(freeblks) struct freeblks *freeblks; { struct inode tip; ufs_daddr_t bn; struct fs *fs; int i, level, bsize; long nblocks, blocksreleased = 0; int error, allerror = 0; ufs_lbn_t baselbns[NIADDR], tmpval;  tip.i_fs = fs = VFSTOUFS(freeblks->fb_mnt)->um_fs; tip.i_number = freeblks->fb_previousinum; tip.i_devvp = freeblks->fb_devvp; tip.i_dev = freeblks->fb_devvp->v_rdev; tip.i_ffs_size = freeblks->fb_oldsize; tip.i_ffs_uid = freeblks->fb_uid; tip.i_vnode = NULL; tmpval = 1; baselbns[0] = NDADDR; for (i = 1; i < NIADDR; i++) { tmpval *= NINDIR(fs); baselbns[i] = baselbns[i - 1] + tmpval; } nblocks = btodb(fs->fs_bsize); blocksreleased = 0;  for (level = (NIADDR - 1); level >= 0; level--) { if ((bn = freeblks->fb_iblks[level]) == 0) continue; if ((error = indir_trunc(&tip, fsbtodb(fs, bn), level, baselbns[level], &blocksreleased)) == 0) allerror = error; ffs_blkfree(&tip, bn, fs->fs_bsize); blocksreleased += nblocks; }  for (i = (NDADDR - 1); i >= 0; i--) { if ((bn = freeblks->fb_dblks[i]) == 0) continue; bsize = blksize(fs, &tip, i); ffs_blkfree(&tip, bn, bsize); blocksreleased += btodb(bsize); }  #ifdef DIAGNOSTIC if (freeblks->fb_chkcnt != blocksreleased) printf("handle_workitem_freeblocks: block count"); if (allerror) softdep_error("handle_workitem_freeblks", allerror); #endif  WORKITEM_FREE(freeblks, D_FREEBLKS); }   STATIC int indir_trunc(ip, dbn, level, lbn, countp) struct inode *ip; ufs_daddr_t dbn; int level; ufs_lbn_t lbn; long *countp; { struct buf *bp; ufs_daddr_t *bap; ufs_daddr_t nb; struct fs *fs; struct worklist *wk; struct indirdep *indirdep; int i, lbnadd, nblocks; int error, allerror = 0;  fs = ip->i_fs; lbnadd = 1; for (i = level; i > 0; i--) lbnadd *= NINDIR(fs);  ACQUIRE_LOCK(&lk); if ((bp = incore(ip->i_devvp, dbn)) != NULL && (wk = LIST_FIRST(&bp->b_dep)) != NULL) { if (wk->wk_type != D_INDIRDEP || (indirdep = WK_INDIRDEP(wk))->ir_savebp != bp || (indirdep->ir_state & GOINGAWAY) == 0) { FREE_LOCK(&lk); panic("indir_trunc: lost indirdep"); } WORKLIST_REMOVE(wk); WORKITEM_FREE(indirdep, D_INDIRDEP); if (LIST_FIRST(&bp->b_dep) != NULL) { FREE_LOCK(&lk); panic("indir_trunc: dangling dep"); } FREE_LOCK(&lk); } else { FREE_LOCK(&lk); error = bread(ip->i_devvp, dbn, (int)fs->fs_bsize, NOCRED, &bp); if (error) return (error); }  bap = (ufs_daddr_t *)bp->b_data; nblocks = btodb(fs->fs_bsize); for (i = NINDIR(fs) - 1; i >= 0; i--) { if ((nb = bap[i]) == 0) continue; if (level != 0) { if ((error = indir_trunc(ip, fsbtodb(fs, nb), level - 1, lbn + (i * lbnadd), countp)) != 0) allerror = error; } ffs_blkfree(ip, nb, fs->fs_bsize); *countp += nblocks; } bp->b_flags |= B_INVAL | B_NOCACHE; brelse(bp); return (allerror); }   STATIC void free_allocindir(aip, inodedep) struct allocindir *aip; struct inodedep *inodedep; { struct freefrag *freefrag;  #ifdef DEBUG if (lk.lkt_held == -1) panic("free_allocindir: lock not held"); #endif if ((aip->ai_state & DEPCOMPLETE) == 0) LIST_REMOVE(aip, ai_deps); if (aip->ai_state & ONWORKLIST) WORKLIST_REMOVE(&aip->ai_list); LIST_REMOVE(aip, ai_next); if ((freefrag = aip->ai_freefrag) != NULL) { if (inodedep == NULL) add_to_worklist(&freefrag->ff_list); else WORKLIST_INSERT(&inodedep->id_bufwait, &freefrag->ff_list); } WORKITEM_FREE(aip, D_ALLOCINDIR); }     void softdep_setup_directory_add(bp, dp, diroffset, newinum, newdirbp) struct buf *bp;		 struct inode *dp;	 off_t diroffset;	 long newinum;		 struct buf *newdirbp;	 { int offset;		 ufs_lbn_t lbn;		 struct fs *fs; struct diradd *dap; struct pagedep *pagedep; struct inodedep *inodedep; struct mkdir *mkdir1, *mkdir2;   if (newinum == WINO) { if (newdirbp != NULL) bdwrite(newdirbp); return; }  fs = dp->i_fs; lbn = lblkno(fs, diroffset); offset = blkoff(fs, diroffset); MALLOC(dap, struct diradd *, sizeof(struct diradd), M_DIRADD, M_SOFTDEP_FLAGS); bzero(dap,sizeof(struct diradd)); dap->da_list.wk_type = D_DIRADD; dap->da_offset = offset; dap->da_newinum = newinum; dap->da_state = ATTACHED; if (newdirbp == NULL) { dap->da_state |= DEPCOMPLETE; ACQUIRE_LOCK(&lk); } else { dap->da_state |= MKDIR_BODY | MKDIR_PARENT; MALLOC(mkdir1, struct mkdir *, sizeof(struct mkdir), M_MKDIR, M_SOFTDEP_FLAGS); mkdir1->md_list.wk_type = D_MKDIR; mkdir1->md_state = MKDIR_BODY; mkdir1->md_diradd = dap; MALLOC(mkdir2, struct mkdir *, sizeof(struct mkdir), M_MKDIR, M_SOFTDEP_FLAGS); mkdir2->md_list.wk_type = D_MKDIR; mkdir2->md_state = MKDIR_PARENT; mkdir2->md_diradd = dap;  mkdir1->md_buf = newdirbp; ACQUIRE_LOCK(&lk); LIST_INSERT_HEAD(&mkdirlisthd, mkdir1, md_mkdirs); WORKLIST_INSERT(&newdirbp->b_dep, &mkdir1->md_list); FREE_LOCK(&lk); bdwrite(newdirbp);  ACQUIRE_LOCK(&lk); if (inodedep_lookup(dp->i_fs, dp->i_number, 0, &inodedep) == 0 || (inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) { dap->da_state &= ~MKDIR_PARENT; WORKITEM_FREE(mkdir2, D_MKDIR); } else { LIST_INSERT_HEAD(&mkdirlisthd, mkdir2, md_mkdirs); WORKLIST_INSERT(&inodedep->id_bufwait,&mkdir2->md_list); } }  if (pagedep_lookup(dp, lbn, DEPALLOC, &pagedep) == 0) WORKLIST_INSERT(&bp->b_dep, &pagedep->pd_list); dap->da_pagedep = pagedep; LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(offset)], dap, da_pdlist);  (void) inodedep_lookup(fs, newinum, DEPALLOC, &inodedep); if ((inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) diradd_inode_written(dap, inodedep); else WORKLIST_INSERT(&inodedep->id_bufwait, &dap->da_list); FREE_LOCK(&lk); }   void softdep_change_directoryentry_offset(dp, base, oldloc, newloc, entrysize) struct inode *dp;	 caddr_t base;		 caddr_t oldloc;		 caddr_t newloc;		 int entrysize;		 { int offset, oldoffset, newoffset; struct pagedep *pagedep; struct diradd *dap; ufs_lbn_t lbn;  ACQUIRE_LOCK(&lk); lbn = lblkno(dp->i_fs, dp->i_offset); offset = blkoff(dp->i_fs, dp->i_offset); if (pagedep_lookup(dp, lbn, 0, &pagedep) == 0) goto done; oldoffset = offset + (oldloc - base); newoffset = offset + (newloc - base);  LIST_FOREACH(dap, &pagedep->pd_diraddhd[DIRADDHASH(oldoffset)], da_pdlist) { if (dap->da_offset != oldoffset) continue; dap->da_offset = newoffset; if (DIRADDHASH(newoffset) == DIRADDHASH(oldoffset)) break; LIST_REMOVE(dap, da_pdlist); LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(newoffset)], dap, da_pdlist); break; } if (dap == NULL) {  LIST_FOREACH(dap, &pagedep->pd_pendinghd, da_pdlist) { if (dap->da_offset == oldoffset) { dap->da_offset = newoffset; break; } } } done: bcopy(oldloc, newloc, entrysize); FREE_LOCK(&lk); }   STATIC void free_diradd(dap) struct diradd *dap; { struct dirrem *dirrem; struct pagedep *pagedep; struct inodedep *inodedep; struct mkdir *mkdir, *nextmd;  #ifdef DEBUG if (lk.lkt_held == -1) panic("free_diradd: lock not held"); #endif WORKLIST_REMOVE(&dap->da_list); LIST_REMOVE(dap, da_pdlist); if ((dap->da_state & DIRCHG) == 0) { pagedep = dap->da_pagedep; } else { dirrem = dap->da_previous; pagedep = dirrem->dm_pagedep; dirrem->dm_dirinum = pagedep->pd_ino; add_to_worklist(&dirrem->dm_list); } if (inodedep_lookup(VFSTOUFS(pagedep->pd_mnt)->um_fs, dap->da_newinum, 0, &inodedep) != 0) (void) free_inodedep(inodedep); if ((dap->da_state & (MKDIR_PARENT | MKDIR_BODY)) != 0) { for (mkdir = LIST_FIRST(&mkdirlisthd); mkdir; mkdir = nextmd) { nextmd = LIST_NEXT(mkdir, md_mkdirs); if (mkdir->md_diradd != dap) continue; dap->da_state &= ~mkdir->md_state; WORKLIST_REMOVE(&mkdir->md_list); LIST_REMOVE(mkdir, md_mkdirs); WORKITEM_FREE(mkdir, D_MKDIR); } if ((dap->da_state & (MKDIR_PARENT | MKDIR_BODY)) != 0) { FREE_LOCK(&lk); panic("free_diradd: unfound ref"); } } WORKITEM_FREE(dap, D_DIRADD); }     void softdep_setup_remove(bp, dp, ip, isrmdir) struct buf *bp;		 struct inode *dp;	 struct inode *ip;	 int isrmdir;		 { struct dirrem *dirrem, *prevdirrem;   dirrem = newdirrem(bp, dp, ip, isrmdir, &prevdirrem);   if ((dirrem->dm_state & COMPLETE) == 0) { LIST_INSERT_HEAD(&dirrem->dm_pagedep->pd_dirremhd, dirrem, dm_next); FREE_LOCK(&lk); } else { if (prevdirrem != NULL) LIST_INSERT_HEAD(&dirrem->dm_pagedep->pd_dirremhd, prevdirrem, dm_next); dirrem->dm_dirinum = dirrem->dm_pagedep->pd_ino; FREE_LOCK(&lk); handle_workitem_remove(dirrem); } }   STATIC long num_dirrem;		 STATIC struct dirrem *
newdirrem(bp, dp, ip, isrmdir, prevdirremp)	struct buf *bp;		/* buffer containing directory block */	struct inode *dp;	/* inode for the directory being modified */	struct inode *ip;	/* inode for directory entry being removed */	int isrmdir;		/* indicates if doing RMDIR */	struct dirrem **prevdirremp; /* previously referenced inode, if any */{	int offset;	ufs_lbn_t lbn;	struct diradd *dap;	struct dirrem *dirrem;	struct pagedep *pagedep;	/*	 * Whiteouts have no deletion dependencies.	 */	if (ip == NULL)		panic("newdirrem: whiteout");	/*	 * If we are over our limit, try to improve the situation.	 * Limiting the number of dirrem structures will also limit	 * the number of freefile and freeblks structures.	 */	if (num_dirrem > max_softdeps / 2)		(void) request_cleanup(FLUSH_REMOVE, 0);	num_dirrem += 1;	MALLOC(dirrem, struct dirrem *, sizeof(struct dirrem),		M_DIRREM, M_SOFTDEP_FLAGS);	bzero(dirrem,sizeof(struct dirrem));	dirrem->dm_list.wk_type = D_DIRREM;	dirrem->dm_state = isrmdir ? RMDIR : 0;	dirrem->dm_mnt = ITOV(ip)->v_mount;	dirrem->dm_oldinum = ip->i_number;	*prevdirremp = NULL;	ACQUIRE_LOCK(&lk);	lbn = lblkno(dp->i_fs, dp->i_offset);	offset = blkoff(dp->i_fs, dp->i_offset);	if (pagedep_lookup(dp, lbn, DEPALLOC, &pagedep) == 0)		WORKLIST_INSERT(&bp->b_dep, &pagedep->pd_list);	dirrem->dm_pagedep = pagedep;	/*	 * Check for a diradd dependency for the same directory entry.	 * If present, then both dependencies become obsolete and can	 * be de-allocated. Check for an entry on both the pd_dirraddhd	 * list and the pd_pendinghd list.	 */	LIST_FOREACH(dap, &pagedep->pd_diraddhd[DIRADDHASH(offset)], da_pdlist)		if (dap->da_offset == offset)			break;	if (dap == NULL) {		LIST_FOREACH(dap, &pagedep->pd_pendinghd, da_pdlist)			if (dap->da_offset == offset)				break;		if (dap == NULL)			return (dirrem);	}	/*	 * Must be ATTACHED at this point.	 */	if ((dap->da_state & ATTACHED) == 0) {		FREE_LOCK(&lk);		panic("newdirrem: not ATTACHED");	}	if (dap->da_newinum != ip->i_number) {		FREE_LOCK(&lk);		panic("newdirrem: inum %d should be %d",		    ip->i_number, dap->da_newinum);	}	/*	 * If we are deleting a changed name that never made it to disk,	 * then return the dirrem describing the previous inode (which	 * represents the inode currently referenced from this entry on disk).	 */	if ((dap->da_state & DIRCHG) != 0) {		*prevdirremp = dap->da_previous;		dap->da_state &= ~DIRCHG;		dap->da_pagedep = pagedep;	}	/*	 * We are deleting an entry that never made it to disk.	 * Mark it COMPLETE so we can delete its inode immediately.	 */	dirrem->dm_state |= COMPLETE;	free_diradd(dap);	return (dirrem);}