Changeset View
Changeset View
Standalone View
Standalone View
sys/ufs/ffs/ffs_vfsops.c
Show First 20 Lines • Show All 275 Lines • ▼ Show 20 Lines | if (ppsratecheck(&ump->um_last_integritymsg, | ||||
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. | * On first ENXIO error, initiate an asynchronous forcible unmount. | ||||
* Used to unmount filesystems whose underlying media has gone away. | * 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; | |||||
etp = v; | |||||
/* | |||||
* Find our mount and get a ref on it, then try to unmount. | |||||
*/ | |||||
mp = vfs_getvfs(&etp->fsid); | |||||
if (mp != NULL) | |||||
dounmount(mp, MNT_FORCE, curthread); | |||||
free(etp, M_UFSMNT); | |||||
} | |||||
/* | |||||
* On first ENXIO error, start a task that forcibly unmounts the filesystem. | |||||
* | * | ||||
jah: It seems like we should be able to clean the FFS error handling code up quite a bit by just… | |||||
Not Done Inline ActionsI concur with your suggestion. mckusick: I concur with your suggestion. | |||||
Done Inline ActionsIs there a good way to test the fsfail case? I've shied away from doing the cleanup mentioned here without a good way to test it. jah: Is there a good way to test the fsfail case? I've shied away from doing the cleanup mentioned… | |||||
Not Done Inline Actions
Peter Holm has some good tests that start up activity on a UFS filesystem and then kill the underlying disk device. mckusick: > Is there a good way to test the fsfail case? I've shied away from doing the cleanup… | |||||
* Return true if a cleanup is in progress. | * Return true if a cleanup is in progress. | ||||
*/ | */ | ||||
int | int | ||||
ffs_fsfail_cleanup(struct ufsmount *ump, int error) | ffs_fsfail_cleanup(struct ufsmount *ump, int error) | ||||
{ | { | ||||
int retval; | int retval; | ||||
UFS_LOCK(ump); | UFS_LOCK(ump); | ||||
retval = ffs_fsfail_cleanup_locked(ump, error); | retval = ffs_fsfail_cleanup_locked(ump, error); | ||||
UFS_UNLOCK(ump); | UFS_UNLOCK(ump); | ||||
return (retval); | return (retval); | ||||
} | } | ||||
int | int | ||||
ffs_fsfail_cleanup_locked(struct ufsmount *ump, int error) | ffs_fsfail_cleanup_locked(struct ufsmount *ump, int error) | ||||
{ | { | ||||
struct fsfail_task *etp; | |||||
struct task *tp; | |||||
mtx_assert(UFS_MTX(ump), MA_OWNED); | mtx_assert(UFS_MTX(ump), MA_OWNED); | ||||
if (error == ENXIO && (ump->um_flags & UM_FSFAIL_CLEANUP) == 0) { | if (error == ENXIO && (ump->um_flags & UM_FSFAIL_CLEANUP) == 0) { | ||||
ump->um_flags |= UM_FSFAIL_CLEANUP; | ump->um_flags |= UM_FSFAIL_CLEANUP; | ||||
/* | /* | ||||
* Queue an async forced unmount. | * Queue an async forced unmount. | ||||
*/ | */ | ||||
etp = ump->um_fsfail_task; | vfs_ref(ump->um_mountp); | ||||
ump->um_fsfail_task = NULL; | dounmount(ump->um_mountp, | ||||
if (etp != NULL) { | MNT_FORCE | MNT_RECURSE | MNT_TASKQUEUE, curthread); | ||||
tp = &etp->task; | |||||
TASK_INIT(tp, 0, ffs_fsfail_unmount, etp); | |||||
taskqueue_enqueue(taskqueue_thread, tp); | |||||
printf("UFS: forcibly unmounting %s from %s\n", | printf("UFS: forcibly unmounting %s from %s\n", | ||||
ump->um_mountp->mnt_stat.f_mntfromname, | ump->um_mountp->mnt_stat.f_mntfromname, | ||||
ump->um_mountp->mnt_stat.f_mntonname); | ump->um_mountp->mnt_stat.f_mntonname); | ||||
} | } | ||||
} | |||||
return ((ump->um_flags & UM_FSFAIL_CLEANUP) != 0); | return ((ump->um_flags & UM_FSFAIL_CLEANUP) != 0); | ||||
} | } | ||||
/* | /* | ||||
* Wrapper used during ENXIO cleanup to allocate empty buffers when | * 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 kernel is unable to read the real one. They are needed so that | ||||
* the soft updates code can use them to unwind its dependencies. | * the soft updates code can use them to unwind its dependencies. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 690 Lines • ▼ Show 20 Lines | ffs_mountfs(odevvp, mp, td) | ||||
struct ufsmount *ump; | struct ufsmount *ump; | ||||
struct fs *fs; | struct fs *fs; | ||||
struct cdev *dev; | struct cdev *dev; | ||||
int error, i, len, ronly; | int error, i, len, ronly; | ||||
struct ucred *cred; | struct ucred *cred; | ||||
struct g_consumer *cp; | struct g_consumer *cp; | ||||
struct mount *nmp; | struct mount *nmp; | ||||
struct vnode *devvp; | struct vnode *devvp; | ||||
struct fsfail_task *etp; | |||||
int candelete, canspeedup; | int candelete, canspeedup; | ||||
off_t loc; | off_t loc; | ||||
fs = NULL; | fs = NULL; | ||||
ump = NULL; | ump = NULL; | ||||
cred = td ? td->td_ucred : NOCRED; | cred = td ? td->td_ucred : NOCRED; | ||||
ronly = (mp->mnt_flag & MNT_RDONLY) != 0; | ronly = (mp->mnt_flag & MNT_RDONLY) != 0; | ||||
▲ Show 20 Lines • Show All 271 Lines • ▼ Show 20 Lines | #ifdef UFS_EXTATTR_AUTOSTART | ||||
* an attribute of the same name. | * an attribute of the same name. | ||||
* Not clear how to report errors -- probably eat them. | * Not clear how to report errors -- probably eat them. | ||||
* This would all happen while the filesystem was busy/not | * This would all happen while the filesystem was busy/not | ||||
* available, so would effectively be "atomic". | * available, so would effectively be "atomic". | ||||
*/ | */ | ||||
(void) ufs_extattr_autostart(mp, td); | (void) ufs_extattr_autostart(mp, td); | ||||
#endif /* !UFS_EXTATTR_AUTOSTART */ | #endif /* !UFS_EXTATTR_AUTOSTART */ | ||||
#endif /* !UFS_EXTATTR */ | #endif /* !UFS_EXTATTR */ | ||||
etp = malloc(sizeof *ump->um_fsfail_task, M_UFSMNT, M_WAITOK | M_ZERO); | |||||
etp->fsid = mp->mnt_stat.f_fsid; | |||||
ump->um_fsfail_task = etp; | |||||
return (0); | return (0); | ||||
out: | out: | ||||
if (fs != NULL) { | if (fs != NULL) { | ||||
free(fs->fs_csp, M_UFSMNT); | free(fs->fs_csp, M_UFSMNT); | ||||
free(fs->fs_si, M_UFSMNT); | free(fs->fs_si, M_UFSMNT); | ||||
free(fs, M_UFSMNT); | free(fs, M_UFSMNT); | ||||
} | } | ||||
if (cp != NULL) { | if (cp != NULL) { | ||||
▲ Show 20 Lines • Show All 230 Lines • ▼ Show 20 Lines | #endif | ||||
mtx_destroy(UFS_MTX(ump)); | mtx_destroy(UFS_MTX(ump)); | ||||
if (mp->mnt_gjprovider != NULL) { | if (mp->mnt_gjprovider != NULL) { | ||||
free(mp->mnt_gjprovider, M_UFSMNT); | free(mp->mnt_gjprovider, M_UFSMNT); | ||||
mp->mnt_gjprovider = NULL; | mp->mnt_gjprovider = NULL; | ||||
} | } | ||||
free(fs->fs_csp, M_UFSMNT); | free(fs->fs_csp, M_UFSMNT); | ||||
free(fs->fs_si, M_UFSMNT); | free(fs->fs_si, M_UFSMNT); | ||||
free(fs, M_UFSMNT); | free(fs, M_UFSMNT); | ||||
if (ump->um_fsfail_task != NULL) | |||||
free(ump->um_fsfail_task, M_UFSMNT); | |||||
free(ump, M_UFSMNT); | free(ump, M_UFSMNT); | ||||
mp->mnt_data = NULL; | mp->mnt_data = NULL; | ||||
MNT_ILOCK(mp); | MNT_ILOCK(mp); | ||||
mp->mnt_flag &= ~MNT_LOCAL; | mp->mnt_flag &= ~MNT_LOCAL; | ||||
MNT_IUNLOCK(mp); | MNT_IUNLOCK(mp); | ||||
if (td->td_su == mp) { | if (td->td_su == mp) { | ||||
td->td_su = NULL; | td->td_su = NULL; | ||||
vfs_rel(mp); | vfs_rel(mp); | ||||
▲ Show 20 Lines • Show All 1,157 Lines • Show Last 20 Lines |
It seems like we should be able to clean the FFS error handling code up quite a bit by just directly issuing dounmount with MNT_FORCE | MNT_RECURSE | MNT_TASKQUEUE from the failing context. That would allow us to get rid of the fsfail_task construct and would also avoid blocking the global taskqueue_thread with a potentially-expensive recursive unmount.