]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/commit
MFC of 232351, 233438, and 233629
authormckusick <mckusick@ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f>
Wed, 28 Mar 2012 21:34:55 +0000 (21:34 +0000)
committermckusick <mckusick@ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f>
Wed, 28 Mar 2012 21:34:55 +0000 (21:34 +0000)
commite432f0981132ee8bfdd94cf7feb77d8737b39d59
tree63b3de119969a343be7f460cdd155b22bec3cb0c
parent70743681231701c04d97db08f77f7c011508bf91
MFC of 232351, 233438, and 233629

MFC reviewed by: kib

MFC 232351:

This change avoids a kernel deadlock on "snaplk" when using
snapshots on UFS filesystems running with journaled soft updates.
This is the first of several bugs that need to be fixed before
removing the restriction added in -r230250 to prevent the use
of snapshots on filesystems running with journaled soft updates.

The deadlock occurs when holding the snapshot lock (snaplk)
and then trying to flush an inode via ffs_update(). We become
blocked by another process trying to flush a different inode
contained in the same inode block that we need. It holds the
inode block for which we are waiting locked. When it tries to
write the inode block, it gets blocked waiting for the our
snaplk when it calls ffs_copyonwrite() to see if the inode
block needs to be copied in our snapshot.

The most obvious place that this deadlock arises is in the
ffs_copyonwrite() routine when it updates critical metadata
in a snapshot and tries to write it out before proceeding.
The fix here is to write the data and indirect block pointer
for the snapshot, but to skip the call to ffs_update() to
write the snapshot inode. To ensure that we will never have
to update a pointer in the inode itself, the ffs_snapshot()
routine that creates the snapshot has to ensure that all the
direct blocks are allocated as part of the creation of the
snapshot.

A less obvious place that this deadlock occurs is when we hold
the snaplk because we are deleting a snapshot. In the course of
doing the deletion, we need to allocate various soft update
dependency structures and allocate some journal space. If we
hit a resource limit while doing this we decrease the resources
in use by flushing out an existing dirty file to get it to give
up the soft dependency resources that it holds. The flush can
cause an ffs_update() to be done on the inode for the file that
we have selected to flush resulting in the same deadlock as
described above when the inode that we have chosen to flush
resides in the same inode block as the snapshot inode that we hold.
The fix is to defer cleaning up any time that the inode on which
we are operating is a snapshot.

Help and review by:    Jeff Roberson
Tested by:             Peter Holm

MFC 233438:

Add a third flags argument to ffs_syncvnode to avoid a possible conflict
with MNT_WAIT flags that passed in its second argument.

Discussed with: kib

MFC 233629:

A refinement of change 232351 to avoid a race with a forcible unmount.
While we have a snapshot vnode unlocked to avoid a deadlock with another
inode in the same inode block being updated, the filesystem containing
it may be forcibly unmounted. When that happens the snapshot vnode is
revoked. We need to check for that condition and fail appropriately.

Spotted by:  kib
Reviewed by: kib

git-svn-id: svn://svn.freebsd.org/base/stable/9@233630 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f
sys/kern/vfs_bio.c
sys/sys/buf.h
sys/ufs/ffs/ffs_balloc.c
sys/ufs/ffs/ffs_extern.h
sys/ufs/ffs/ffs_inode.c
sys/ufs/ffs/ffs_rawread.c
sys/ufs/ffs/ffs_snapshot.c
sys/ufs/ffs/ffs_softdep.c
sys/ufs/ffs/ffs_vfsops.c
sys/ufs/ffs/ffs_vnops.c
sys/ufs/ufs/inode.h