aboutsummaryrefslogtreecommitdiff
path: root/sys/ufs/ffs/ffs_vnops.c
diff options
context:
space:
mode:
authorChuck Silvers <chs@FreeBSD.org>2020-05-25 23:47:31 +0000
committerChuck Silvers <chs@FreeBSD.org>2020-05-25 23:47:31 +0000
commitd79ff54b5caad61e657b9cc651aa256f3949b97a (patch)
tree24dde841c02021c0b6542edf676014835312fcf0 /sys/ufs/ffs/ffs_vnops.c
parentb02676a2cbf7dc2fe7c1eb2895667fa846030cd8 (diff)
downloadsrc-d79ff54b5caad61e657b9cc651aa256f3949b97a.tar.gz
src-d79ff54b5caad61e657b9cc651aa256f3949b97a.zip
This commit enables a UFS filesystem to do a forcible unmount when
the underlying media fails or becomes inaccessible. For example when a USB flash memory card hosting a UFS filesystem is unplugged. The strategy for handling disk I/O errors when soft updates are enabled is to stop writing to the disk of the affected file system but continue to accept I/O requests and report that all future writes by the file system to that disk actually succeed. Then initiate an asynchronous forced unmount of the affected file system. There are two cases for disk I/O errors: - ENXIO, which means that this disk is gone and the lower layers of the storage stack already guarantee that no future I/O to this disk will succeed. - EIO (or most other errors), which means that this particular I/O request has failed but subsequent I/O requests to this disk might still succeed. For ENXIO, we can just clear the error and continue, because we know that the file system cannot affect the on-disk state after we see this error. For EIO or other errors, we arrange for the geom_vfs layer to reject all future I/O requests with ENXIO just like is done when the geom_vfs is orphaned. In both cases, the file system code can just clear the error and proceed with the forcible unmount. This new treatment of I/O errors is needed for writes of any buffer that is involved in a dependency. Most dependencies are described by a structure attached to the buffer's b_dep field. But some are created and processed as a result of the completion of the dependencies attached to the buffer. Clearing of some dependencies require a read. For example if there is a dependency that requires an inode to be written, the disk block containing that inode must be read, the updated inode copied into place in that buffer, and the buffer then written back to disk. Often the needed buffer is already in memory and can be used. But if it needs to be read from the disk, the read will fail, so we fabricate a buffer full of zeroes and pretend that the read succeeded. This zero'ed buffer can be updated and written back to disk. The only case where a buffer full of zeros causes the code to do the wrong thing is when reading an inode buffer containing an inode that still has an inode dependency in memory that will reinitialize the effective link count (i_effnlink) based on the actual link count (i_nlink) that we read. To handle this case we now store the i_nlink value that we wrote in the inode dependency so that it can be restored into the zero'ed buffer thus keeping the tracking of the inode link count consistent. Because applications depend on knowing when an attempt to write their data to stable storage has failed, the fsync(2) and msync(2) system calls need to return errors if data fails to be written to stable storage. So these operations return ENXIO for every call made on files in a file system where we have otherwise been ignoring I/O errors. Coauthered by: mckusick Reviewed by: kib Tested by: Peter Holm Approved by: mckusick (mentor) Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D24088
Notes
Notes: svn path=/head/; revision=361491
Diffstat (limited to 'sys/ufs/ffs/ffs_vnops.c')
-rw-r--r--sys/ufs/ffs/ffs_vnops.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c
index 8a1d13802e31..7c1df1f50e31 100644
--- a/sys/ufs/ffs/ffs_vnops.c
+++ b/sys/ufs/ffs/ffs_vnops.c
@@ -239,6 +239,8 @@ retry:
}
BO_UNLOCK(bo);
}
+ if (ffs_fsfail_cleanup(VFSTOUFS(vp->v_mount), 0))
+ return (ENXIO);
return (0);
}
@@ -247,6 +249,7 @@ ffs_syncvnode(struct vnode *vp, int waitfor, int flags)
{
struct inode *ip;
struct bufobj *bo;
+ struct ufsmount *ump;
struct buf *bp, *nbp;
ufs_lbn_t lbn;
int error, passes;
@@ -255,14 +258,18 @@ ffs_syncvnode(struct vnode *vp, int waitfor, int flags)
ip = VTOI(vp);
ip->i_flag &= ~IN_NEEDSYNC;
bo = &vp->v_bufobj;
+ ump = VFSTOUFS(vp->v_mount);
/*
* When doing MNT_WAIT we must first flush all dependencies
* on the inode.
*/
if (DOINGSOFTDEP(vp) && waitfor == MNT_WAIT &&
- (error = softdep_sync_metadata(vp)) != 0)
+ (error = softdep_sync_metadata(vp)) != 0) {
+ if (ffs_fsfail_cleanup(ump, error))
+ error = 0;
return (error);
+ }
/*
* Flush all dirty buffers associated with a vnode.
@@ -332,7 +339,10 @@ loop:
}
if (wait) {
bremfree(bp);
- if ((error = bwrite(bp)) != 0)
+ error = bwrite(bp);
+ if (ffs_fsfail_cleanup(ump, error))
+ error = 0;
+ if (error != 0)
return (error);
} else if ((bp->b_flags & B_CLUSTEROK)) {
(void) vfs_bio_awrite(bp);
@@ -901,8 +911,11 @@ ffs_write(ap)
uio->uio_offset -= resid - uio->uio_resid;
uio->uio_resid = resid;
}
- } else if (resid > uio->uio_resid && (ioflag & IO_SYNC))
+ } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) {
error = ffs_update(vp, 1);
+ if (ffs_fsfail_cleanup(VFSTOUFS(vp->v_mount), error))
+ error = ENXIO;
+ }
return (error);
}