Changeset View
Standalone View
sys/ufs/ffs/ffs_subr.c
Show First 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | if (I_IS_UFS1(ip)) { | ||||
ip->i_size = dip1->di_size; | ip->i_size = dip1->di_size; | ||||
ip->i_flags = dip1->di_flags; | ip->i_flags = dip1->di_flags; | ||||
ip->i_gen = dip1->di_gen; | ip->i_gen = dip1->di_gen; | ||||
ip->i_uid = dip1->di_uid; | ip->i_uid = dip1->di_uid; | ||||
ip->i_gid = dip1->di_gid; | ip->i_gid = dip1->di_gid; | ||||
return (0); | return (0); | ||||
} | } | ||||
dip2 = ((struct ufs2_dinode *)bp->b_data + ino_to_fsbo(fs, ino)); | dip2 = ((struct ufs2_dinode *)bp->b_data + ino_to_fsbo(fs, ino)); | ||||
if ((error = ffs_verify_dinode_ckhash(fs, dip2)) != 0) { | if ((error = ffs_verify_dinode_ckhash(fs, dip2)) != 0 && | ||||
!ffs_fsfail_cleanup(ITOUMP(ip), error)) { | |||||
printf("%s: inode %jd: check-hash failed\n", fs->fs_fsmnt, | printf("%s: inode %jd: check-hash failed\n", fs->fs_fsmnt, | ||||
(intmax_t)ino); | (intmax_t)ino); | ||||
return (error); | return (error); | ||||
} | } | ||||
*ip->i_din2 = *dip2; | *ip->i_din2 = *dip2; | ||||
dip2 = ip->i_din2; | dip2 = ip->i_din2; | ||||
ip->i_mode = dip2->di_mode; | ip->i_mode = dip2->di_mode; | ||||
ip->i_nlink = dip2->di_nlink; | ip->i_nlink = dip2->di_nlink; | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | if (ppsratecheck(&ump->um_last_integritymsg, | ||||
UFS_UNLOCK(ump); | UFS_UNLOCK(ump); | ||||
uprintf("\n%s: inode %jd, out-of-range indirect block " | uprintf("\n%s: inode %jd, out-of-range indirect block " | ||||
"number %jd\n", mp->mnt_stat.f_mntonname, inum, daddr); | "number %jd\n", mp->mnt_stat.f_mntonname, inum, daddr); | ||||
if (havemtx) | if (havemtx) | ||||
UFS_LOCK(ump); | UFS_LOCK(ump); | ||||
} else if (!havemtx) | } else if (!havemtx) | ||||
UFS_UNLOCK(ump); | UFS_UNLOCK(ump); | ||||
return (EINTEGRITY); | return (EINTEGRITY); | ||||
} | |||||
/* | |||||
* Initiate a forcible unmount. | |||||
* Used to unmount filesystems whose underlying media has gone away. | |||||
*/ | |||||
static void | |||||
ffs_fsfail_unmount(void *v, int pending) | |||||
{ | |||||
struct fsfail_task *etp; | |||||
struct mount *mp; | |||||
int error; | |||||
etp = v; | |||||
/* | |||||
* Find our mount and get a ref on it, then try to unmount. | |||||
*/ | |||||
do { | |||||
mp = vfs_getvfs(&etp->fsid); | |||||
if (mp == NULL) | |||||
break; | |||||
error = dounmount(mp, MNT_FORCE, curthread); | |||||
} while (error != 0); | |||||
kib: Why do it in loop ? Why do you expect further attempts to succeed if one of them failed ?
I… | |||||
chsAuthorUnsubmitted Done Inline ActionsMy recollection is that I wanted to handle a race with another unmount. If this automatic unmount failed because a manual one is in progress and then the manual one fails (perhaps because it is not a forced unmount), then we would want to retry the automatic one. But looking again now, I see that racing unmounts are serialized by the vnode lock of mp->mnt_vnodecovered, so the scenario I just described cannot happen. We previously discussed one situation that currently causes forced unmounts to fail, which is if the mount is being used by a null mount (ie. mnt_uppers is not NULL). Kirk wants to have dounmount() internally unmount any mnt_uppers recursively, and once I implement that then a forced unmount should never fail. I'll change this to not retry the unmount. chs: My recollection is that I wanted to handle a race with another unmount. If this automatic… | |||||
free(etp, M_UFSMNT); | |||||
} | |||||
/* | |||||
* On first ENXIO error, start a task that forcibly unmounts the filesystem. | |||||
* | |||||
* Return true if a cleanup is in progress. | |||||
*/ | |||||
int | |||||
ffs_fsfail_cleanup(struct ufsmount *ump, int error) | |||||
{ | |||||
int retval; | |||||
UFS_LOCK(ump); | |||||
retval = ffs_fsfail_cleanup_locked(ump, error); | |||||
UFS_UNLOCK(ump); | |||||
return (retval); | |||||
} | |||||
int | |||||
ffs_fsfail_cleanup_locked(struct ufsmount *ump, int error) | |||||
{ | |||||
struct fsfail_task *etp; | |||||
struct task *tp; | |||||
mtx_assert(UFS_MTX(ump), MA_OWNED); | |||||
if (error == ENXIO && (ump->um_flags & UM_FSFAIL_CLEANUP) == 0) { | |||||
ump->um_flags |= UM_FSFAIL_CLEANUP; | |||||
/* | |||||
* Queue an async forced unmount. | |||||
*/ | |||||
etp = ump->um_fsfail_task; | |||||
ump->um_fsfail_task = NULL; | |||||
if (etp != NULL) { | |||||
tp = &etp->task; | |||||
TASK_INIT(tp, 0, ffs_fsfail_unmount, etp); | |||||
taskqueue_enqueue(taskqueue_thread, tp); | |||||
kibUnsubmitted Not Done Inline ActionsDont' we need to drain the task somewhere ? Obvious required place is on ufs.ko unload. kib: Dont' we need to drain the task somewhere ? Obvious required place is on ufs.ko unload. | |||||
chsAuthorUnsubmitted Done Inline ActionsThat's true, I'll add that. chs: That's true, I'll add that. | |||||
printf("UFS: forcibly unmounting %s from %s\n", | |||||
ump->um_mountp->mnt_stat.f_mntfromname, | |||||
ump->um_mountp->mnt_stat.f_mntonname); | |||||
} | |||||
} | |||||
return ((ump->um_flags & UM_FSFAIL_CLEANUP) != 0); | |||||
} | |||||
/* | |||||
* Wrapper used during ENXIO cleanup to allocate empty buffers when | |||||
* the kernel is unable to read the real one. They are needed so that | |||||
* the soft updates code can use them to unwind its dependencies. | |||||
*/ | |||||
int | |||||
ffs_breadz(struct ufsmount *ump, struct vnode *vp, daddr_t lblkno, | |||||
daddr_t dblkno, int size, daddr_t *rablkno, int *rabsize, int cnt, | |||||
struct ucred *cred, int flags, void (*ckhashfunc)(struct buf *), | |||||
struct buf **bpp) | |||||
{ | |||||
int error; | |||||
flags |= GB_CVTENXIO; | |||||
error = breadn_flags(vp, lblkno, dblkno, size, rablkno, rabsize, cnt, | |||||
cred, flags, ckhashfunc, bpp); | |||||
if (error != 0 && ffs_fsfail_cleanup(ump, error)) { | |||||
error = getblkx(vp, lblkno, dblkno, size, 0, 0, flags, bpp); | |||||
if (error != 0) { | |||||
printf("%s: unexpected error %d from getblkx\n", | |||||
kibUnsubmitted Not Done Inline ActionsShouldn't this printf be either removed or changed to KASSERT ? I believe there is more than one such place. kib: Shouldn't this printf be either removed or changed to KASSERT ? I believe there is more than… | |||||
chsAuthorUnsubmitted Done Inline ActionsI'll change these to KASSERTs. chs: I'll change these to KASSERTs. | |||||
__func__, error); | |||||
return ENXIO; | |||||
kibUnsubmitted Not Done Inline Actionsreturn (ENXIO); kib: return (ENXIO); | |||||
chsAuthorUnsubmitted Done Inline ActionsI'll change it. chs: I'll change it. | |||||
} | |||||
bzero((*bpp)->b_data, size); | |||||
kibUnsubmitted Not Done Inline ActionsWhy not use vfs_bio_bzero_buf() ? Or, if this function is not supposed to get unmapped buffer, assert it. kib: Why not use vfs_bio_bzero_buf() ? Or, if this function is not supposed to get unmapped buffer… | |||||
chsAuthorUnsubmitted Done Inline ActionsI didn't know about vfs_bio_bzero_buf() (and apparently neither did Kirk). I'll switch to using that. chs: I didn't know about vfs_bio_bzero_buf() (and apparently neither did Kirk). I'll switch to… | |||||
} | |||||
return error; | |||||
} | } | ||||
#endif /* _KERNEL */ | #endif /* _KERNEL */ | ||||
/* | /* | ||||
* Verify an inode check-hash. | * Verify an inode check-hash. | ||||
*/ | */ | ||||
int | int | ||||
ffs_verify_dinode_ckhash(struct fs *fs, struct ufs2_dinode *dip) | ffs_verify_dinode_ckhash(struct fs *fs, struct ufs2_dinode *dip) | ||||
▲ Show 20 Lines • Show All 528 Lines • Show Last 20 Lines |
Why do it in loop ? Why do you expect further attempts to succeed if one of them failed ?
I am afraid that one failure implies further failures, and then taskqueue thread eats CPU and deadlocks system because other tasks cannot be run.