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;		 struct inode *dp;	 struct inode *ip;	 int isrmdir;		 struct dirrem **prevdirremp;  { int offset; ufs_lbn_t lbn; struct diradd *dap; struct dirrem *dirrem; struct pagedep *pagedep;   if (ip == NULL) panic("newdirrem: whiteout");  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;   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); }  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 ((dap->da_state & DIRCHG) != 0) { *prevdirremp = dap->da_previous; dap->da_state &= ~DIRCHG; dap->da_pagedep = pagedep; }  dirrem->dm_state |= COMPLETE; free_diradd(dap); return (dirrem); }     void softdep_setup_directory_change(bp, dp, ip, newinum, isrmdir) struct buf *bp;		 struct inode *dp;	 struct inode *ip;	 long newinum;		 int isrmdir;		 { int offset; struct diradd *dap = NULL; struct dirrem *dirrem, *prevdirrem; struct pagedep *pagedep; struct inodedep *inodedep;  offset = blkoff(dp->i_fs, dp->i_offset);   if (newinum != WINO) { 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_state = DIRCHG | ATTACHED | DEPCOMPLETE; dap->da_offset = offset; dap->da_newinum = newinum; }   dirrem = newdirrem(bp, dp, ip, isrmdir, &prevdirrem); pagedep = dirrem->dm_pagedep;  if (isrmdir > 1) dirrem->dm_state |= DIRCHG;   if (newinum == WINO) { if ((dirrem->dm_state & COMPLETE) == 0) { LIST_INSERT_HEAD(&pagedep->pd_dirremhd, dirrem, dm_next); } else { dirrem->dm_dirinum = pagedep->pd_ino; add_to_worklist(&dirrem->dm_list); } FREE_LOCK(&lk); return; }   if ((dirrem->dm_state & COMPLETE) == 0) { dap->da_previous = dirrem; } else { if (prevdirrem != NULL) { dap->da_previous = prevdirrem; } else { dap->da_state &= ~DIRCHG; dap->da_pagedep = pagedep; } dirrem->dm_dirinum = pagedep->pd_ino; add_to_worklist(&dirrem->dm_list); }  if (inodedep_lookup(dp->i_fs, newinum, DEPALLOC, &inodedep) == 0 || (inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) { dap->da_state |= COMPLETE; LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist); WORKLIST_INSERT(&inodedep->id_pendinghd, &dap->da_list); } else { LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(offset)], dap, da_pdlist); WORKLIST_INSERT(&inodedep->id_bufwait, &dap->da_list); } FREE_LOCK(&lk); }   void softdep_change_linkcnt(ip) struct inode *ip;	 { struct inodedep *inodedep;  ACQUIRE_LOCK(&lk); (void) inodedep_lookup(ip->i_fs, ip->i_number, DEPALLOC, &inodedep); if (ip->i_ffs_nlink < ip->i_effnlink) { FREE_LOCK(&lk); panic("softdep_change_linkcnt: bad delta"); } inodedep->id_nlinkdelta = ip->i_ffs_nlink - ip->i_effnlink; FREE_LOCK(&lk); }   STATIC void handle_workitem_remove(dirrem) struct dirrem *dirrem; { struct proc *p = CURPROC;	 struct inodedep *inodedep; struct vnode *vp; struct inode *ip; ino_t oldinum; int error;  if ((error = VFS_VGET(dirrem->dm_mnt, dirrem->dm_oldinum, &vp)) != 0) { softdep_error("handle_workitem_remove: vget", error); return; } ip = VTOI(vp); ACQUIRE_LOCK(&lk); if ((inodedep_lookup(ip->i_fs, dirrem->dm_oldinum, 0, &inodedep)) == 0) { FREE_LOCK(&lk); panic("handle_workitem_remove: lost inodedep"); }  if ((dirrem->dm_state & RMDIR) == 0) { ip->i_ffs_nlink--; ip->i_flag |= IN_CHANGE; if (ip->i_ffs_nlink < ip->i_effnlink) { FREE_LOCK(&lk); panic("handle_workitem_remove: bad file delta"); } inodedep->id_nlinkdelta = ip->i_ffs_nlink - ip->i_effnlink; FREE_LOCK(&lk); vput(vp); num_dirrem -= 1; WORKITEM_FREE(dirrem, D_DIRREM); return; }  ip->i_ffs_nlink -= 2; ip->i_flag |= IN_CHANGE; if (ip->i_ffs_nlink < ip->i_effnlink) panic("handle_workitem_remove: bad dir delta"); inodedep->id_nlinkdelta = ip->i_ffs_nlink - ip->i_effnlink; FREE_LOCK(&lk); if ((error = UFS_TRUNCATE(ip, (off_t)0, 0, p->p_ucred)) != 0) softdep_error("handle_workitem_remove: truncate", error);  if (dirrem->dm_state & DIRCHG) { vput(vp); num_dirrem -= 1; WORKITEM_FREE(dirrem, D_DIRREM); return; }  ACQUIRE_LOCK(&lk); dirrem->dm_state = 0; oldinum = dirrem->dm_oldinum; dirrem->dm_oldinum = dirrem->dm_dirinum; if (inodedep_lookup(ip->i_fs, oldinum, 0, &inodedep) == 0 || check_inode_unwritten(inodedep)) { FREE_LOCK(&lk); vput(vp); handle_workitem_remove(dirrem); return; } WORKLIST_INSERT(&inodedep->id_inowait, &dirrem->dm_list); FREE_LOCK(&lk); vput(vp); }   STATIC void handle_workitem_freefile(freefile) struct freefile *freefile; { struct fs *fs; struct inode tip; #ifdef DEBUG struct inodedep *idp; #endif int error;  fs = VFSTOUFS(freefile->fx_mnt)->um_fs; #ifdef DEBUG ACQUIRE_LOCK(&lk); error = inodedep_lookup(fs, freefile->fx_oldinum, 0, &idp); FREE_LOCK(&lk); if (error) panic("handle_workitem_freefile: inodedep survived"); #endif tip.i_devvp = freefile->fx_devvp; tip.i_dev = freefile->fx_devvp->v_rdev; tip.i_fs = fs; tip.i_vnode = NULL;  if ((error = ffs_freefile(&tip, freefile->fx_oldinum, freefile->fx_mode)) != 0) { softdep_error("handle_workitem_freefile", error); } WORKITEM_FREE(freefile, D_FREEFILE); }   void softdep_disk_io_initiation(bp) struct buf *bp;		 { struct worklist *wk, *nextwk; struct indirdep *indirdep;   if (bp->b_flags & B_READ) panic("softdep_disk_io_initiation: read");  for (wk = LIST_FIRST(&bp->b_dep); wk; wk = nextwk) { nextwk = LIST_NEXT(wk, wk_list); switch (wk->wk_type) {  case D_PAGEDEP: initiate_write_filepage(WK_PAGEDEP(wk), bp); continue;  case D_INODEDEP: initiate_write_inodeblock(WK_INODEDEP(wk), bp); continue;  case D_INDIRDEP: indirdep = WK_INDIRDEP(wk); if (indirdep->ir_state & GOINGAWAY) panic("disk_io_initiation: indirdep gone");  if (LIST_FIRST(&indirdep->ir_deplisthd) == NULL) { indirdep->ir_savebp->b_flags |= B_INVAL | B_NOCACHE; brelse(indirdep->ir_savebp);  wk->wk_state &= ~ONWORKLIST; LIST_REMOVE(wk, wk_list); WORKITEM_FREE(indirdep, D_INDIRDEP); continue; }  MALLOC(indirdep->ir_saveddata, caddr_t, bp->b_bcount, M_INDIRDEP, M_SOFTDEP_FLAGS); ACQUIRE_LOCK(&lk); indirdep->ir_state &= ~ATTACHED; indirdep->ir_state |= UNDONE; bcopy(bp->b_data, indirdep->ir_saveddata, bp->b_bcount); bcopy(indirdep->ir_savebp->b_data, bp->b_data, bp->b_bcount); FREE_LOCK(&lk); continue;  case D_MKDIR: case D_BMSAFEMAP: case D_ALLOCDIRECT: case D_ALLOCINDIR: continue;  default: panic("handle_disk_io_initiation: Unexpected type %s", TYPENAME(wk->wk_type));  } } }   STATIC void initiate_write_filepage(pagedep, bp) struct pagedep *pagedep; struct buf *bp; { struct diradd *dap; struct direct *ep; int i;  if (pagedep->pd_state & IOSTARTED) {  printf("initiate_write_filepage: already started\n"); return; } pagedep->pd_state |= IOSTARTED; ACQUIRE_LOCK(&lk); for (i = 0; i < DAHASHSZ; i++) { LIST_FOREACH(dap, &pagedep->pd_diraddhd[i], da_pdlist) { ep = (struct direct *) ((char *)bp->b_data + dap->da_offset); if (ep->d_ino != dap->da_newinum) { FREE_LOCK(&lk); panic("%s: dir inum %d != new %d", "initiate_write_filepage", ep->d_ino, dap->da_newinum); } if (dap->da_state & DIRCHG) ep->d_ino = dap->da_previous->dm_oldinum; else ep->d_ino = 0; dap->da_state &= ~ATTACHED; dap->da_state |= UNDONE; } } FREE_LOCK(&lk); }   STATIC void initiate_write_inodeblock(inodedep, bp) struct inodedep *inodedep; struct buf *bp;			 { struct allocdirect *adp, *lastadp; struct dinode *dp; struct fs *fs; ufs_lbn_t prevlbn = 0; int i, deplist;  if (inodedep->id_state & IOSTARTED) panic("initiate_write_inodeblock: already started"); inodedep->id_state |= IOSTARTED; fs = inodedep->id_fs; dp = (struct dinode *)bp->b_data + ino_to_fsbo(fs, inodedep->id_ino);  if ((inodedep->id_state & DEPCOMPLETE) == 0) { if (inodedep->id_savedino != NULL) panic("initiate_write_inodeblock: already doing I/O"); MALLOC(inodedep->id_savedino, struct dinode *, sizeof(struct dinode), M_INODEDEP, M_SOFTDEP_FLAGS); *inodedep->id_savedino = *dp; bzero((caddr_t)dp, sizeof(struct dinode)); return; }  inodedep->id_savedsize = dp->di_size; if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL) return;  ACQUIRE_LOCK(&lk); for (deplist = 0, adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp; adp = TAILQ_NEXT(adp, ad_next)) { #ifdef DIAGNOSTIC if (deplist != 0 && prevlbn >= adp->ad_lbn) { FREE_LOCK(&lk); panic("softdep_write_inodeblock: lbn order"); } prevlbn = adp->ad_lbn; if (adp->ad_lbn < NDADDR && dp->di_db[adp->ad_lbn] != adp->ad_newblkno) { FREE_LOCK(&lk); panic("%s: direct pointer #%ld mismatch %d != %d", "softdep_write_inodeblock", adp->ad_lbn, dp->di_db[adp->ad_lbn], adp->ad_newblkno); } if (adp->ad_lbn >= NDADDR && dp->di_ib[adp->ad_lbn - NDADDR] != adp->ad_newblkno) { FREE_LOCK(&lk); panic("%s: indirect pointer #%ld mismatch %d != %d", "softdep_write_inodeblock", adp->ad_lbn - NDADDR, dp->di_ib[adp->ad_lbn - NDADDR], adp->ad_newblkno); } deplist |= 1 << adp->ad_lbn; if ((adp->ad_state & ATTACHED) == 0) { FREE_LOCK(&lk); panic("softdep_write_inodeblock: Unknown state 0x%x", adp->ad_state); } #endif  adp->ad_state &= ~ATTACHED; adp->ad_state |= UNDONE; }  for (lastadp = NULL, adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp; lastadp = adp, adp = TAILQ_NEXT(adp, ad_next)) { if (adp->ad_lbn >= NDADDR) break; dp->di_db[adp->ad_lbn] = adp->ad_oldblkno;  if (adp->ad_oldsize == 0 || adp->ad_oldsize == fs->fs_bsize) continue; dp->di_size = fs->fs_bsize * adp->ad_lbn + adp->ad_oldsize; for (i = adp->ad_lbn + 1; i < NDADDR; i++) { #ifdef DIAGNOSTIC if (dp->di_db[i] != 0 && (deplist & (1 << i)) == 0) { FREE_LOCK(&lk); panic("softdep_write_inodeblock: lost dep1"); } #endif  dp->di_db[i] = 0; } for (i = 0; i < NIADDR; i++) { #ifdef DIAGNOSTIC if (dp->di_ib[i] != 0 && (deplist & ((1 << NDADDR) << i)) == 0) { FREE_LOCK(&lk); panic("softdep_write_inodeblock: lost dep2"); } #endif  dp->di_ib[i] = 0; } FREE_LOCK(&lk); return; }  if (lastadp != NULL && dp->di_size <= (lastadp->ad_lbn + 1) * fs->fs_bsize) { for (i = lastadp->ad_lbn; i >= 0; i--) if (dp->di_db[i] != 0) break; dp->di_size = (i + 1) * fs->fs_bsize; }  for (; adp; adp = TAILQ_NEXT(adp, ad_next)) dp->di_ib[adp->ad_lbn - NDADDR] = 0; FREE_LOCK(&lk); }   void softdep_disk_write_complete(bp) struct buf *bp;		 { struct worklist *wk; struct workhead reattach; struct newblk *newblk; struct allocindir *aip; struct allocdirect *adp; struct indirdep *indirdep; struct inodedep *inodedep; struct bmsafemap *bmsafemap;  #ifdef DEBUG if (lk.lkt_held != -1) panic("softdep_disk_write_complete: lock is held"); lk.lkt_held = -2; #endif LIST_INIT(&reattach); while ((wk = LIST_FIRST(&bp->b_dep)) != NULL) { WORKLIST_REMOVE(wk); switch (wk->wk_type) {  case D_PAGEDEP: if (handle_written_filepage(WK_PAGEDEP(wk), bp)) WORKLIST_INSERT(&reattach, wk); continue;  case D_INODEDEP: if (handle_written_inodeblock(WK_INODEDEP(wk), bp)) WORKLIST_INSERT(&reattach, wk); continue;  case D_BMSAFEMAP: bmsafemap = WK_BMSAFEMAP(wk); while ((newblk = LIST_FIRST(&bmsafemap->sm_newblkhd))) { newblk->nb_state |= DEPCOMPLETE; newblk->nb_bmsafemap = NULL; LIST_REMOVE(newblk, nb_deps); } while ((adp = LIST_FIRST(&bmsafemap->sm_allocdirecthd))) { adp->ad_state |= DEPCOMPLETE; adp->ad_buf = NULL; LIST_REMOVE(adp, ad_deps); handle_allocdirect_partdone(adp); } while ((aip = LIST_FIRST(&bmsafemap->sm_allocindirhd))) { aip->ai_state |= DEPCOMPLETE; aip->ai_buf = NULL; LIST_REMOVE(aip, ai_deps); handle_allocindir_partdone(aip); } while ((inodedep = LIST_FIRST(&bmsafemap->sm_inodedephd)) != NULL) { inodedep->id_state |= DEPCOMPLETE; LIST_REMOVE(inodedep, id_deps); inodedep->id_buf = NULL; } WORKITEM_FREE(bmsafemap, D_BMSAFEMAP); continue;  case D_MKDIR: handle_written_mkdir(WK_MKDIR(wk), MKDIR_BODY); continue;  case D_ALLOCDIRECT: adp = WK_ALLOCDIRECT(wk); adp->ad_state |= COMPLETE; handle_allocdirect_partdone(adp); continue;  case D_ALLOCINDIR: aip = WK_ALLOCINDIR(wk); aip->ai_state |= COMPLETE; handle_allocindir_partdone(aip); continue;  case D_INDIRDEP: indirdep = WK_INDIRDEP(wk); if (indirdep->ir_state & GOINGAWAY) panic("disk_write_complete: indirdep gone"); bcopy(indirdep->ir_saveddata, bp->b_data, bp->b_bcount); FREE(indirdep->ir_saveddata, M_INDIRDEP); indirdep->ir_saveddata = 0; indirdep->ir_state &= ~UNDONE; indirdep->ir_state |= ATTACHED; while ((aip = LIST_FIRST(&indirdep->ir_donehd)) != 0) { handle_allocindir_partdone(aip); if (aip == LIST_FIRST(&indirdep->ir_donehd)) panic("disk_write_complete: not gone"); } WORKLIST_INSERT(&reattach, wk); if ((bp->b_flags & B_DELWRI) == 0) stat_indir_blk_ptrs++; buf_dirty(bp); continue;  default: panic("handle_disk_write_complete: Unknown type %s", TYPENAME(wk->wk_type));  } }  while ((wk = LIST_FIRST(&reattach)) != NULL) { WORKLIST_REMOVE(wk); WORKLIST_INSERT(&bp->b_dep, wk); } #ifdef DEBUG if (lk.lkt_held != -2) panic("softdep_disk_write_complete: lock lost"); lk.lkt_held = -1; #endif }   STATIC void handle_allocdirect_partdone(adp) struct allocdirect *adp;	 { struct allocdirect *listadp; struct inodedep *inodedep; long bsize, delay;  if ((adp->ad_state & ALLCOMPLETE) != ALLCOMPLETE) return; if (adp->ad_buf != NULL) panic("handle_allocdirect_partdone: dangling dep");   inodedep = adp->ad_inodedep; bsize = inodedep->id_fs->fs_bsize; TAILQ_FOREACH(listadp, &inodedep->id_inoupdt, ad_next) {  if (listadp == adp) break;  if (listadp->ad_oldsize == 0 || listadp->ad_oldsize == bsize) continue;  return; }  if (listadp == NULL) { #ifdef DEBUG TAILQ_FOREACH(listadp, &inodedep->id_newinoupdt, ad_next)  if (listadp == adp) break; if (listadp == NULL) panic("handle_allocdirect_partdone: lost dep"); #endif  return; }  delay = (inodedep->id_state & DEPCOMPLETE); for (; adp; adp = listadp) { listadp = TAILQ_NEXT(adp, ad_next); if ((adp->ad_state & ALLCOMPLETE) != ALLCOMPLETE) return; free_allocdirect(&inodedep->id_inoupdt, adp, delay); } }   STATIC void handle_allocindir_partdone(aip) struct allocindir *aip;		 { struct indirdep *indirdep;  if ((aip->ai_state & ALLCOMPLETE) != ALLCOMPLETE) return; if (aip->ai_buf != NULL) panic("handle_allocindir_partdone: dangling dependency"); indirdep = aip->ai_indirdep; if (indirdep->ir_state & UNDONE) { LIST_REMOVE(aip, ai_next); LIST_INSERT_HEAD(&indirdep->ir_donehd, aip, ai_next); return; } ((ufs_daddr_t *)indirdep->ir_savebp->b_data)[aip->ai_offset] = aip->ai_newblkno; LIST_REMOVE(aip, ai_next); if (aip->ai_freefrag != NULL) add_to_worklist(&aip->ai_freefrag->ff_list); WORKITEM_FREE(aip, D_ALLOCINDIR); }   STATIC int handle_written_inodeblock(inodedep, bp) struct inodedep *inodedep; struct buf *bp;		 { struct worklist *wk, *filefree; struct allocdirect *adp, *nextadp; struct dinode *dp; int hadchanges;  if ((inodedep->id_state & IOSTARTED) == 0) panic("handle_written_inodeblock: not started"); inodedep->id_state &= ~IOSTARTED; inodedep->id_state |= COMPLETE; dp = (struct dinode *)bp->b_data + ino_to_fsbo(inodedep->id_fs, inodedep->id_ino);  if (inodedep->id_savedino != NULL) { *dp = *inodedep->id_savedino; FREE(inodedep->id_savedino, M_INODEDEP); inodedep->id_savedino = NULL; if ((bp->b_flags & B_DELWRI) == 0) stat_inode_bitmap++; buf_dirty(bp); return (1); }  hadchanges = 0; for (adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp; adp = nextadp) { nextadp = TAILQ_NEXT(adp, ad_next); if (adp->ad_state & ATTACHED) panic("handle_written_inodeblock: new entry"); if (adp->ad_lbn < NDADDR) { if (dp->di_db[adp->ad_lbn] != adp->ad_oldblkno) panic("%s: %s #%ld mismatch %d != %d", "handle_written_inodeblock", "direct pointer", adp->ad_lbn, dp->di_db[adp->ad_lbn], adp->ad_oldblkno); dp->di_db[adp->ad_lbn] = adp->ad_newblkno; } else { if (dp->di_ib[adp->ad_lbn - NDADDR] != 0) panic("%s: %s #%ld allocated as %d", "handle_written_inodeblock", "indirect pointer", adp->ad_lbn - NDADDR, dp->di_ib[adp->ad_lbn - NDADDR]); dp->di_ib[adp->ad_lbn - NDADDR] = adp->ad_newblkno; } adp->ad_state &= ~UNDONE; adp->ad_state |= ATTACHED; hadchanges = 1; } if (hadchanges && (bp->b_flags & B_DELWRI) == 0) stat_direct_blk_ptrs++;  if (inodedep->id_savedsize == -1) panic("handle_written_inodeblock: bad size"); if (dp->di_size != inodedep->id_savedsize) { dp->di_size = inodedep->id_savedsize; hadchanges = 1; } inodedep->id_savedsize = -1;  if (hadchanges) buf_dirty(bp);  if ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != NULL) handle_allocdirect_partdone(adp);  filefree = NULL; while ((wk = LIST_FIRST(&inodedep->id_bufwait)) != NULL) { WORKLIST_REMOVE(wk); switch (wk->wk_type) {  case D_FREEFILE:  if (filefree != NULL) panic("handle_written_inodeblock: filefree"); filefree = wk; continue;  case D_MKDIR: handle_written_mkdir(WK_MKDIR(wk), MKDIR_PARENT); continue;  case D_DIRADD: diradd_inode_written(WK_DIRADD(wk), inodedep); continue;  case D_FREEBLKS: case D_FREEFRAG: case D_DIRREM: add_to_worklist(wk); continue;  default: panic("handle_written_inodeblock: Unknown type %s", TYPENAME(wk->wk_type));  } } if (filefree != NULL) { if (free_inodedep(inodedep) == 0) panic("handle_written_inodeblock: live inodedep"); add_to_worklist(filefree); return (0); }   if (free_inodedep(inodedep) || TAILQ_FIRST(&inodedep->id_inoupdt) == 0) return (0); return (hadchanges); }   STATIC void diradd_inode_written(dap, inodedep) struct diradd *dap; struct inodedep *inodedep; { struct pagedep *pagedep;  dap->da_state |= COMPLETE; if ((dap->da_state & ALLCOMPLETE) == ALLCOMPLETE) { if (dap->da_state & DIRCHG) pagedep = dap->da_previous->dm_pagedep; else pagedep = dap->da_pagedep; LIST_REMOVE(dap, da_pdlist); LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist); } WORKLIST_INSERT(&inodedep->id_pendinghd, &dap->da_list); }   STATIC void handle_written_mkdir(mkdir, type) struct mkdir *mkdir; int type; { struct diradd *dap; struct pagedep *pagedep;  if (mkdir->md_state != type) panic("handle_written_mkdir: bad type"); dap = mkdir->md_diradd; dap->da_state &= ~type; if ((dap->da_state & (MKDIR_PARENT | MKDIR_BODY)) == 0) dap->da_state |= DEPCOMPLETE; if ((dap->da_state & ALLCOMPLETE) == ALLCOMPLETE) { if (dap->da_state & DIRCHG) pagedep = dap->da_previous->dm_pagedep; else pagedep = dap->da_pagedep; LIST_REMOVE(dap, da_pdlist); LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist); } LIST_REMOVE(mkdir, md_mkdirs); WORKITEM_FREE(mkdir, D_MKDIR); }   STATIC int handle_written_filepage(pagedep, bp) struct pagedep *pagedep; struct buf *bp;		 { struct dirrem *dirrem; struct diradd *dap, *nextdap; struct direct *ep; int i, chgs;  if ((pagedep->pd_state & IOSTARTED) == 0) panic("handle_written_filepage: not started"); pagedep->pd_state &= ~IOSTARTED;  while ((dirrem = LIST_FIRST(&pagedep->pd_dirremhd)) != NULL) { LIST_REMOVE(dirrem, dm_next); dirrem->dm_dirinum = pagedep->pd_ino; add_to_worklist(&dirrem->dm_list); }  while ((dap = LIST_FIRST(&pagedep->pd_pendinghd)) != NULL) free_diradd(dap);  for (chgs = 0, i = 0; i < DAHASHSZ; i++) { for (dap = LIST_FIRST(&pagedep->pd_diraddhd[i]); dap; dap = nextdap) { nextdap = LIST_NEXT(dap, da_pdlist); if (dap->da_state & ATTACHED) panic("handle_written_filepage: attached"); ep = (struct direct *) ((char *)bp->b_data + dap->da_offset); ep->d_ino = dap->da_newinum; dap->da_state &= ~UNDONE; dap->da_state |= ATTACHED; chgs = 1;  if ((dap->da_state & ALLCOMPLETE) == ALLCOMPLETE) { LIST_REMOVE(dap, da_pdlist); LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist); } } }  if (chgs) { if ((bp->b_flags & B_DELWRI) == 0) stat_dir_entry++; buf_dirty(bp); }  if (LIST_FIRST(&pagedep->pd_pendinghd) == 0) { for (i = 0; i < DAHASHSZ; i++) if (LIST_FIRST(&pagedep->pd_diraddhd[i]) != NULL) break; if (i == DAHASHSZ) { LIST_REMOVE(pagedep, pd_hash); WORKITEM_FREE(pagedep, D_PAGEDEP); return (0); } } return (1); }     void softdep_load_inodeblock(ip) struct inode *ip;	 { struct inodedep *inodedep;   ip->i_effnlink = ip->i_ffs_nlink; ACQUIRE_LOCK(&lk); if (inodedep_lookup(ip->i_fs, ip->i_number, 0, &inodedep) == 0) { FREE_LOCK(&lk); return; } ip->i_effnlink -= inodedep->id_nlinkdelta; FREE_LOCK(&lk); }   void softdep_update_inodeblock(ip, bp, waitfor) struct inode *ip;	 struct buf *bp;		 int waitfor;		 { struct inodedep *inodedep; struct worklist *wk; int error, gotit;   ACQUIRE_LOCK(&lk); if (inodedep_lookup(ip->i_fs, ip->i_number, 0, &inodedep) == 0) { FREE_LOCK(&lk); if (ip->i_effnlink != ip->i_ffs_nlink) panic("softdep_update_inodeblock: bad link count"); return; } if (inodedep->id_nlinkdelta != ip->i_ffs_nlink - ip->i_effnlink) { FREE_LOCK(&lk); panic("softdep_update_inodeblock: bad delta"); }  inodedep->id_state &= ~COMPLETE; if ((inodedep->id_state & ONWORKLIST) == 0) WORKLIST_INSERT(&bp->b_dep, &inodedep->id_list);  merge_inode_lists(inodedep); if (TAILQ_FIRST(&inodedep->id_inoupdt) != NULL) handle_allocdirect_partdone(TAILQ_FIRST(&inodedep->id_inoupdt));  while ((wk = LIST_FIRST(&inodedep->id_inowait)) != NULL) { WORKLIST_REMOVE(wk); WORKLIST_INSERT(&inodedep->id_bufwait, wk); }  if ((inodedep->id_state & DEPCOMPLETE) != 0 || waitfor == 0) { FREE_LOCK(&lk); return; } gotit = getdirtybuf(&inodedep->id_buf, MNT_WAIT); FREE_LOCK(&lk); if (gotit && (error = bwrite(inodedep->id_buf)) != 0) softdep_error("softdep_update_inodeblock: bwrite", error); if ((inodedep->id_state & DEPCOMPLETE) == 0) panic("softdep_update_inodeblock: update failed"); }   STATIC void merge_inode_lists(inodedep) struct inodedep *inodedep; { struct allocdirect *listadp, *newadp;  newadp = TAILQ_FIRST(&inodedep->id_newinoupdt); for (listadp = TAILQ_FIRST(&inodedep->id_inoupdt); listadp && newadp;) { if (listadp->ad_lbn < newadp->ad_lbn) { listadp = TAILQ_NEXT(listadp, ad_next); continue; } TAILQ_REMOVE(&inodedep->id_newinoupdt, newadp, ad_next); TAILQ_INSERT_BEFORE(listadp, newadp, ad_next); if (listadp->ad_lbn == newadp->ad_lbn) { allocdirect_merge(&inodedep->id_inoupdt, newadp, listadp); listadp = newadp; } newadp = TAILQ_FIRST(&inodedep->id_newinoupdt); } while ((newadp = TAILQ_FIRST(&inodedep->id_newinoupdt)) != NULL) { TAILQ_REMOVE(&inodedep->id_newinoupdt, newadp, ad_next); TAILQ_INSERT_TAIL(&inodedep->id_inoupdt, newadp, ad_next); } }   int softdep_fsync(vp) struct vnode *vp;	 { struct inodedep *inodedep; struct pagedep *pagedep; struct worklist *wk; struct diradd *dap; struct mount *mnt; struct vnode *pvp; struct inode *ip; struct buf *bp; struct fs *fs; struct proc *p = CURPROC;		 int error, flushparent; ino_t parentino; ufs_lbn_t lbn;  ip = VTOI(vp); fs = ip->i_fs; ACQUIRE_LOCK(&lk); if (inodedep_lookup(fs, ip->i_number, 0, &inodedep) == 0) { FREE_LOCK(&lk); return (0); } if (LIST_FIRST(&inodedep->id_inowait) != NULL || LIST_FIRST(&inodedep->id_bufwait) != NULL || TAILQ_FIRST(&inodedep->id_inoupdt) != NULL || TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL) { FREE_LOCK(&lk); panic("softdep_fsync: pending ops"); } for (error = 0, flushparent = 0; ; ) { if ((wk = LIST_FIRST(&inodedep->id_pendinghd)) == NULL) break; if (wk->wk_type != D_DIRADD) { FREE_LOCK(&lk); panic("softdep_fsync: Unexpected type %s", TYPENAME(wk->wk_type)); } dap = WK_DIRADD(wk);  if (dap->da_state & DIRCHG) pagedep = dap->da_previous->dm_pagedep; else pagedep = dap->da_pagedep; mnt = pagedep->pd_mnt; parentino = pagedep->pd_ino; lbn = pagedep->pd_lbn; if ((dap->da_state & (MKDIR_BODY | COMPLETE)) != COMPLETE) { FREE_LOCK(&lk); panic("softdep_fsync: dirty"); } flushparent = dap->da_state & MKDIR_PARENT;  if (vp->v_flag & VXLOCK) break;  FREE_LOCK(&lk); VOP_UNLOCK(vp, 0, p); error = VFS_VGET(mnt, parentino, &pvp); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (error != 0) return (error); if (flushparent) { if ((error = UFS_UPDATE(VTOI(pvp), MNT_WAIT))) { vput(pvp); return (error); } }  error = bread(pvp, lbn, blksize(fs, VTOI(pvp), lbn), p->p_ucred, &bp); if (error == 0) error = bwrite(bp); vput(pvp); if (error != 0) return (error); ACQUIRE_LOCK(&lk); if (inodedep_lookup(fs, ip->i_number, 0, &inodedep) == 0) break; } FREE_LOCK(&lk); return (0); }   void softdep_fsync_mountdev(vp) struct vnode *vp; { struct buf *bp, *nbp; struct worklist *wk;  if (!vn_isdisk(vp, NULL)) panic("softdep_fsync_mountdev: vnode not a disk"); ACQUIRE_LOCK(&lk); for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) { nbp = LIST_NEXT(bp, b_vnbufs);  if (bp->b_flags & B_BUSY) continue; bp->b_flags |= B_BUSY;  if ((bp->b_flags & B_DELWRI) == 0) { FREE_LOCK(&lk); panic("softdep_fsync_mountdev: not dirty"); }  if ((wk = LIST_FIRST(&bp->b_dep)) == NULL || wk->wk_type != D_BMSAFEMAP) { bp->b_flags &= ~B_BUSY; continue; } bremfree(bp); FREE_LOCK(&lk); (void) bawrite(bp); ACQUIRE_LOCK(&lk);  nbp = LIST_FIRST(&vp->v_dirtyblkhd); } drain_output(vp, 1); FREE_LOCK(&lk); }   int softdep_sync_metadata(ap) struct vop_fsync_args  *ap; { struct vnode *vp = ap->a_vp; struct pagedep *pagedep; struct allocdirect *adp; struct allocindir *aip; struct buf *bp, *nbp; struct worklist *wk; int i, error, waitfor;   if (!vn_isdisk(vp, NULL)) { if (!DOINGSOFTDEP(vp)) return (0); } else if (vp->v_specmountpoint == NULL || (vp->v_specmountpoint->mnt_flag & MNT_SOFTDEP) == 0) return (0);  ACQUIRE_LOCK(&lk); if ((error = flush_inodedep_deps(VTOI(vp)->i_fs, VTOI(vp)->i_number))) { FREE_LOCK(&lk); return (error); }  waitfor = MNT_NOWAIT; top: if (getdirtybuf(&LIST_FIRST(&vp->v_dirtyblkhd), MNT_WAIT) == 0) { FREE_LOCK(&lk); return (0); } bp = LIST_FIRST(&vp->v_dirtyblkhd); loop:  LIST_FOREACH(wk, &bp->b_dep, wk_list) { switch (wk->wk_type) {  case D_ALLOCDIRECT: adp = WK_ALLOCDIRECT(wk); if (adp->ad_state & DEPCOMPLETE) break; nbp = adp->ad_buf; if (getdirtybuf(&nbp, waitfor) == 0) break; FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(nbp); } else if ((error = VOP_BWRITE(nbp)) != 0) { bawrite(bp); return (error); } ACQUIRE_LOCK(&lk); break;  case D_ALLOCINDIR: aip = WK_ALLOCINDIR(wk); if (aip->ai_state & DEPCOMPLETE) break; nbp = aip->ai_buf; if (getdirtybuf(&nbp, waitfor) == 0) break; FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(nbp); } else if ((error = VOP_BWRITE(nbp)) != 0) { bawrite(bp); return (error); } ACQUIRE_LOCK(&lk); break;  case D_INDIRDEP: restart:  LIST_FOREACH(aip, &WK_INDIRDEP(wk)->ir_deplisthd, ai_next) { if (aip->ai_state & DEPCOMPLETE) continue; nbp = aip->ai_buf; if (getdirtybuf(&nbp, MNT_WAIT) == 0) goto restart; FREE_LOCK(&lk); if ((error = VOP_BWRITE(nbp)) != 0) { bawrite(bp); return (error); } ACQUIRE_LOCK(&lk); goto restart; } break;  case D_INODEDEP: if ((error = flush_inodedep_deps(WK_INODEDEP(wk)->id_fs, WK_INODEDEP(wk)->id_ino)) != 0) { FREE_LOCK(&lk); bawrite(bp); return (error); } break;  case D_PAGEDEP:  pagedep = WK_PAGEDEP(wk); for (i = 0; i < DAHASHSZ; i++) { if (LIST_FIRST(&pagedep->pd_diraddhd[i]) == 0) continue; if ((error = flush_pagedep_deps(vp, pagedep->pd_mnt, &pagedep->pd_diraddhd[i]))) { FREE_LOCK(&lk); bawrite(bp); return (error); } } break;  case D_MKDIR:  nbp = WK_MKDIR(wk)->md_buf; if (getdirtybuf(&nbp, waitfor) == 0) break; FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(nbp); } else if ((error = VOP_BWRITE(nbp)) != 0) { bawrite(bp); return (error); } ACQUIRE_LOCK(&lk); break;  case D_BMSAFEMAP:  nbp = WK_BMSAFEMAP(wk)->sm_buf; if (getdirtybuf(&nbp, waitfor) == 0) break; FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(nbp); } else if ((error = VOP_BWRITE(nbp)) != 0) { bawrite(bp); return (error); } ACQUIRE_LOCK(&lk); break;  default: FREE_LOCK(&lk); panic("softdep_sync_metadata: Unknown type %s", TYPENAME(wk->wk_type));  } } (void) getdirtybuf(&LIST_NEXT(bp, b_vnbufs), MNT_WAIT); nbp = LIST_NEXT(bp, b_vnbufs); FREE_LOCK(&lk); bawrite(bp); ACQUIRE_LOCK(&lk); if (nbp != NULL) { bp = nbp; goto loop; }  drain_output(vp, 1);  if (waitfor == MNT_NOWAIT) { waitfor = MNT_WAIT; FREE_LOCK(&lk); ACQUIRE_LOCK(&lk); goto top; }   if (LIST_FIRST(&vp->v_dirtyblkhd) == NULL) { FREE_LOCK(&lk); return (0); }  FREE_LOCK(&lk);  if (vn_isdisk(vp, NULL) && vp->v_specmountpoint && !VOP_ISLOCKED(vp) && (error = VFS_SYNC(vp->v_specmountpoint, MNT_WAIT, ap->a_cred, ap->a_p)) != 0) return (error); return (0); }   STATIC int flush_inodedep_deps(fs, ino) struct fs *fs; ino_t ino; { struct inodedep *inodedep; struct allocdirect *adp; int error, waitfor; struct buf *bp; struct vnode *vp; struct uvm_object *uobj;  vp = softdep_lookupvp(fs, ino); #ifdef DIAGNOSTIC if (vp == NULL) panic("flush_inodedep_deps: null vp"); #endif uobj = &vp->v_uvm.u_obj;   for (waitfor = MNT_NOWAIT; ; ) { FREE_LOCK(&lk); ACQUIRE_LOCK(&lk); if (inodedep_lookup(fs, ino, 0, &inodedep) == 0) return (0);   FREE_LOCK(&lk); simple_lock(&uobj->vmobjlock); (uobj->pgops->pgo_flush)(uobj, 0, 0, PGO_ALLPAGES|PGO_CLEANIT| (waitfor == MNT_NOWAIT ? 0: PGO_SYNCIO)); simple_unlock(&uobj->vmobjlock); if (waitfor == MNT_WAIT) { drain_output(vp, 0); } ACQUIRE_LOCK(&lk);  TAILQ_FOREACH(adp, &inodedep->id_inoupdt, ad_next) { if (adp->ad_state & DEPCOMPLETE) continue; bp = adp->ad_buf; if (getdirtybuf(&bp, waitfor) == 0) { if (waitfor == MNT_NOWAIT) continue; break; } FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(bp); } else if ((error = VOP_BWRITE(bp)) != 0) { ACQUIRE_LOCK(&lk); return (error); } ACQUIRE_LOCK(&lk); break; } if (adp != NULL) continue; TAILQ_FOREACH(adp, &inodedep->id_newinoupdt, ad_next) { if (adp->ad_state & DEPCOMPLETE) continue; bp = adp->ad_buf; if (getdirtybuf(&bp, waitfor) == 0) { if (waitfor == MNT_NOWAIT) continue; break; } FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(bp); } else if ((error = VOP_BWRITE(bp)) != 0) { ACQUIRE_LOCK(&lk); return (error); } ACQUIRE_LOCK(&lk); break; } if (adp != NULL) continue;  if (waitfor == MNT_WAIT) break; waitfor = MNT_WAIT; }  if (inodedep_lookup(fs, ino, 0, &inodedep) != 0) (void) free_inodedep(inodedep); return (0); }   STATIC int flush_pagedep_deps(pvp, mp, diraddhdp) struct vnode *pvp; struct mount *mp; struct diraddhd *diraddhdp; { struct proc *p = CURPROC;	 struct inodedep *inodedep; struct ufsmount *ump; struct diradd *dap; struct vnode *vp; int gotit, error = 0; struct buf *bp; ino_t inum;  ump = VFSTOUFS(mp); while ((dap = LIST_FIRST(diraddhdp)) != NULL) {  if (dap->da_state & MKDIR_PARENT) { FREE_LOCK(&lk); if ((error = UFS_UPDATE(VTOI(pvp), MNT_WAIT))) break; ACQUIRE_LOCK(&lk);  if (dap != LIST_FIRST(diraddhdp)) continue; if (dap->da_state & MKDIR_PARENT) { FREE_LOCK(&lk); panic("flush_pagedep_deps: MKDIR_PARENT"); } }  inum = dap->da_newinum; if (dap->da_state & MKDIR_BODY) { FREE_LOCK(&lk); if ((error = VFS_VGET(mp, inum, &vp)) != 0) break; if ((error=VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p)) || (error=VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p))) { vput(vp); break; } drain_output(vp, 0); vput(vp); ACQUIRE_LOCK(&lk);  if (dap != LIST_FIRST(diraddhdp)) continue; if (dap->da_state & MKDIR_BODY) { FREE_LOCK(&lk); panic("flush_pagedep_deps: MKDIR_BODY"); } }  if (inodedep_lookup(ump->um_fs, inum, 0, &inodedep) == 0) { FREE_LOCK(&lk); panic("flush_pagedep_deps: lost inode"); }  if ((inodedep->id_state & DEPCOMPLETE) == 0) { gotit = getdirtybuf(&inodedep->id_buf, MNT_WAIT); FREE_LOCK(&lk); if (gotit && (error = bwrite(inodedep->id_buf)) != 0) break; ACQUIRE_LOCK(&lk); if (dap != LIST_FIRST(diraddhdp)) continue; }  FREE_LOCK(&lk); if ((error = bread(ump->um_devvp, fsbtodb(ump->um_fs, ino_to_fsba(ump->um_fs, inum)), (int)ump->um_fs->fs_bsize, NOCRED, &bp)) != 0) break; if ((error = bwrite(bp)) != 0) break; ACQUIRE_LOCK(&lk);  if (dap == LIST_FIRST(diraddhdp)) { FREE_LOCK(&lk); panic("flush_pagedep_deps: flush failed"); } } if (error) ACQUIRE_LOCK(&lk); return (error); }   int softdep_slowdown(vp) struct vnode *vp; { int max_softdeps_hard;  max_softdeps_hard = max_softdeps * 11 / 10; if (num_dirrem < max_softdeps_hard / 2 && num_inodedep < max_softdeps_hard) return (0); stat_sync_limit_hit += 1; return (1); }   STATIC int request_cleanup(resource, islocked) int resource; int islocked; { struct proc *p = CURPROC; int s;   if (p == filesys_syncer) return (0);  if (num_on_worklist > max_softdeps / 10) { if (islocked) FREE_LOCK(&lk); process_worklist_item(NULL, LK_NOWAIT); process_worklist_item(NULL, LK_NOWAIT); stat_worklist_push += 2; if (islocked) ACQUIRE_LOCK(&lk); return(1); }  if (speedup_syncer()) return(0);  switch (resource) {  case FLUSH_INODES: stat_ino_limit_push += 1; req_clear_inodedeps += 1; stat_countp = &stat_ino_limit_hit; break;  case FLUSH_REMOVE: stat_blk_limit_push += 1; req_clear_remove += 1; stat_countp = &stat_blk_limit_hit; break;  default: if (islocked) FREE_LOCK(&lk); panic("request_cleanup: unknown type"); }  if (islocked == 0) ACQUIRE_LOCK(&lk); proc_waiting += 1; if (!timeout_pending(&proc_waiting_timeout)) timeout_add(&proc_waiting_timeout, tickdelay > 2 ? tickdelay : 2);  s = FREE_LOCK_INTERLOCKED(&lk); (void) tsleep((caddr_t)&proc_waiting, PPAUSE, "softupdate", 0); ACQUIRE_LOCK_INTERLOCKED(&lk, s); proc_waiting -= 1; if (islocked == 0) FREE_LOCK(&lk); return (1); }   void pause_timer(arg) void *arg; {  *stat_countp += 1; wakeup_one(&proc_waiting); if (proc_waiting > 0) timeout_add(&proc_waiting_timeout, tickdelay > 2 ? tickdelay : 2); }   STATIC void clear_remove(p) struct proc *p; { struct pagedep_hashhead *pagedephd; struct pagedep *pagedep; static int next = 0; struct mount *mp; struct vnode *vp; int error, cnt; ino_t ino;  ACQUIRE_LOCK(&lk); for (cnt = 0; cnt < pagedep_hash; cnt++) { pagedephd = &pagedep_hashtbl[next++]; if (next >= pagedep_hash) next = 0; LIST_FOREACH(pagedep, pagedephd, pd_hash) { if (LIST_FIRST(&pagedep->pd_dirremhd) == NULL) continue; mp = pagedep->pd_mnt; ino = pagedep->pd_ino; FREE_LOCK(&lk); #if 0 if (vn_start_write(NULL, &mp, V_NOWAIT) != 0) continue; #endif if ((error = VFS_VGET(mp, ino, &vp)) != 0) { softdep_error("clear_remove: vget", error); #if 0 vn_finished_write(mp); #endif return; } if ((error = VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p))) softdep_error("clear_remove: fsync", error); drain_output(vp, 0); vput(vp); #if 0 vn_finished_write(mp); #endif return; } } FREE_LOCK(&lk); }   STATIC void clear_inodedeps(p) struct proc *p; { struct inodedep_hashhead *inodedephd; struct inodedep *inodedep; static int next = 0; struct mount *mp; struct vnode *vp; struct fs *fs; int error, cnt; ino_t firstino, lastino, ino;  ACQUIRE_LOCK(&lk);  for (cnt = 0; cnt < inodedep_hash; cnt++) { inodedephd = &inodedep_hashtbl[next++]; if (next >= inodedep_hash) next = 0; if ((inodedep = LIST_FIRST(inodedephd)) != NULL) break; } if (inodedep == NULL) { FREE_LOCK(&lk); return; }  fs = inodedep->id_fs; CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) if ((mp->mnt_flag & MNT_SOFTDEP) && fs == VFSTOUFS(mp)->um_fs) break;  firstino = inodedep->id_ino & ~(INOPB(fs) - 1); for (lastino = firstino + INOPB(fs) - 1; lastino > firstino; lastino--) if (inodedep_lookup(fs, lastino, 0, &inodedep) != 0) break;  for (ino = firstino; ino <= lastino; ino++) { if (inodedep_lookup(fs, ino, 0, &inodedep) == 0) continue; FREE_LOCK(&lk); #if 0 if (vn_start_write(NULL, &mp, V_NOWAIT) != 0) continue; #endif if ((error = VFS_VGET(mp, ino, &vp)) != 0) { softdep_error("clear_inodedeps: vget", error); #if 0 vn_finished_write(mp); #endif return; } if (ino == lastino) { if ((error = VOP_FSYNC(vp, p->p_ucred, MNT_WAIT, p))) softdep_error("clear_inodedeps: fsync1", error); } else { if ((error = VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p))) softdep_error("clear_inodedeps: fsync2", error); drain_output(vp, 0); } vput(vp); #if 0 vn_finished_write(mp); #endif ACQUIRE_LOCK(&lk); } FREE_LOCK(&lk); }   int softdep_count_dependencies(bp, wantcount, islocked) struct buf *bp; int wantcount; int islocked; { struct worklist *wk; struct inodedep *inodedep; struct indirdep *indirdep; struct allocindir *aip; struct pagedep *pagedep; struct diradd *dap; int i, retval;  retval = 0; if (!islocked) ACQUIRE_LOCK(&lk); LIST_FOREACH(wk, &bp->b_dep, wk_list) { switch (wk->wk_type) {  case D_INODEDEP: inodedep = WK_INODEDEP(wk); if ((inodedep->id_state & DEPCOMPLETE) == 0) {  retval += 1; if (!wantcount) goto out; } if (TAILQ_FIRST(&inodedep->id_inoupdt)) {  retval += 1; if (!wantcount) goto out; } continue;  case D_INDIRDEP: indirdep = WK_INDIRDEP(wk);  LIST_FOREACH(aip, &indirdep->ir_deplisthd, ai_next) {  retval += 1; if (!wantcount) goto out; } continue;  case D_PAGEDEP: pagedep = WK_PAGEDEP(wk); for (i = 0; i < DAHASHSZ; i++) {  LIST_FOREACH(dap, &pagedep->pd_diraddhd[i], da_pdlist) {  retval += 1; if (!wantcount) goto out; } } continue;  case D_BMSAFEMAP: case D_ALLOCDIRECT: case D_ALLOCINDIR: case D_MKDIR:  continue;  default: if (!islocked) FREE_LOCK(&lk); panic("softdep_check_for_rollback: Unexpected type %s", TYPENAME(wk->wk_type));  } } out: if (!islocked) FREE_LOCK(&lk); return retval; }   STATIC int getdirtybuf(bpp, waitfor) struct buf **bpp; int waitfor; { struct buf *bp; int s;  for (;;) { if ((bp = *bpp) == NULL) return (0); if ((bp->b_flags & B_BUSY) == 0) break; if (waitfor != MNT_WAIT) return (0); bp->b_flags |= B_WANTED; s = FREE_LOCK_INTERLOCKED(&lk); tsleep((caddr_t)bp, PRIBIO + 1, "sdsdty", 0); ACQUIRE_LOCK_INTERLOCKED(&lk, s); } if ((bp->b_flags & B_DELWRI) == 0) return (0); bremfree(bp); bp->b_flags |= B_BUSY; return (1); }   STATIC void drain_output(vp, islocked) struct vnode *vp; int islocked; { int s;  if (!islocked) ACQUIRE_LOCK(&lk); while (vp->v_numoutput) { vp->v_bioflag |= VBIOWAIT; s = FREE_LOCK_INTERLOCKED(&lk); tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "drain_output", 0); ACQUIRE_LOCK_INTERLOCKED(&lk, s); } if (!islocked) FREE_LOCK(&lk); }   void softdep_deallocate_dependencies(bp) struct buf *bp; {  if ((bp->b_flags & B_ERROR) == 0) panic("softdep_deallocate_dependencies: dangling deps"); softdep_error(bp->b_vp->v_mount->mnt_stat.f_mntonname, bp->b_error); panic("softdep_deallocate_dependencies: unrecovered I/O error"); }   void softdep_error(func, error) char *func; int error; {   printf("%s: got error %d while accessing filesystem\n", func, error); }   STATIC struct buf *
softdep_setup_pagecache(ip, lbn, size)	struct inode *ip;	ufs_lbn_t lbn;	long size;{	struct vnode *vp = ITOV(ip);	struct buf *bp;	int s;	/*	 * Enter pagecache dependency buf in hash.	 * Always reset b_resid to be the full amount of data in the block	 * since the caller has the corresponding pages locked and dirty.	 */	bp = softdep_lookup_pcbp(vp, lbn);	if (bp == NULL) {		s = splbio();		bp = pool_get(&sdpcpool, PR_WAITOK);		splx(s);		bp->b_vp = vp;		bp->b_lblkno = lbn;		LIST_INIT(&bp->b_dep);		LIST_INSERT_HEAD(&pcbphashhead[PCBPHASH(vp, lbn)], bp, b_hash);		LIST_INSERT_HEAD(&ip->i_pcbufhd, bp, b_vnbufs);	}	bp->b_bcount = bp->b_resid = size;	return bp;}