File remove problem

Bruce Evans brde at optusnet.com.au
Sat Dec 1 10:33:44 PST 2007


On Sun, 2 Dec 2007, Bruce Evans wrote:

> Here is a non-hackish patch which explains why ignoring MNT_RDONLY in
> the above or in ffs_mount() helps.  It just fixes the confusion between
> IN_MODIFIED and IN_CHANGE in critical places.
>
> % Index: ffs_softdep.c
> ...
>
> Without this change, soft updates depends on IN_CHANGE being converted
> to IN_MODIFIED by ufs_itimes(), but this conversion doesn't happen
> when MNT_RDONLY is set.  With soft updates, changes are often delayed
> until sync time, and when the sync is for mount-update it is done after
> setting MNT_RDONLY so the above doesn't work.
> ...
> Without the above patch, after "mv a b", "fsync b" is insufficient to
> make "mount -u -o ro /f" not corrupt the fs (because the mv generates
> 3 or 4 entries in the worklist, and the fsync generates 0 or 1 more
> and always ends up with 1 or 2 not handled, and then the sync for
> mount-update usually doesn't make any more progress).  I didn't check
> if a short delay after the fsync is sufficient, but expect that it is.
> With the above patch, a loop doing "mv a b; mount -u -o ro /f; fsck
> -n /f; mount -u -o rw /f; mv b a" shows no problems.  However, the
> original problem was without soft updates, so there must be more bugs
> here.

The above seems to fix "mv a b", but for "rm a" it only restores the
previous symptoms of the bug -- the "failed to flush worklist" error
is gone but the "update error" is back.

The following simiilar patch seems to fix "rm a" in 5.2:

% Index: ufs_inode.c
% ===================================================================
% RCS file: /home/ncvs/src/sys/ufs/ufs/ufs_inode.c,v
% retrieving revision 1.53
% diff -u -2 -r1.53 ufs_inode.c
% --- ufs_inode.c	7 Apr 2004 03:47:20 -0000	1.53
% +++ ufs_inode.c	1 Dec 2007 17:26:30 -0000
% @@ -108,5 +111,5 @@
%  		ip->i_mode = 0;
%  		DIP(ip, i_mode) = 0;
% -		ip->i_flag |= IN_CHANGE | IN_UPDATE;
% +		ip->i_flag |= IN_MODIFIED;
%  		if (DOINGSOFTDEP(vp))
%  			softdep_change_linkcnt(ip);

This is in ufs_inactive().  Soft updates calls back here from the sync.
softdep_change_linkcount() just creates a dependency and depends on its
callers to set IN_* flags which will cause i/o to flush the dependency.
Here the caller sets flags that had no effect in the MNT_RDONLY case,
the same as in the previous patch.  I removed the IN_CHANGE and IN_UPDATE
flags completely since they just clobber the previous times (set by a
non-delayed ufs_inactive but probably already clobbered by a delayed
truncation).  (We are clearing out parts of the inode, so the times don't
really matter, but unless something else clears the times it is better
to leave the non-delayed times which record when the update marks for
the userland remove were converted to timestamps.)

Just before the above, the file is truncated.  ffs_truncate() has the
usual confusion between IN_CHANGE and IN_MODIFIED.  It sets
(IN_CHANGE | IN_UPDATE) in 7 different places, but never sets IN_MODIFIED.
In my test case of "rm a; mount -u -o ro /f", this isn't a problem, because
IN_MODIFIED is usually already set set ffs_truncate() doesn't forget to
start i/o.  ffs_truncate() calls ffs_update() with waitfor == 1 in many
cases, but in my test case it always calls ffs_update() with waitfor == 0.
Any call to ffs_update() in it cancels any previous setting of IN_MODIFIED
when modifications are moved to the buffer.  Thus when we forget to set
the IN_MODIFIED in the above code, in the MNT_RDONLY case we always have
inconsistencies:
- non-soft updates case: we have at least a cleared i_rdev and i_mode with
   no means to write them.  Is this enough to cause the original problem?
- soft updates case: we also have a dependency with no means to flush it.

I think this change doesn't help for -current yet, since this code is
unreachable when MNT_RDONLY is set.  The truncation code is also
unreachable.  kib's patch makes both reachable again.

I observe/predict/explain the following error behaviours for soft updates
(still some guesses):
-current the first patch: "/f: update error: blocks 32 files 1"
     (because the truncation and other things aren't done, but worklist
     items aren't created for them so the problem isn't detected in
     softdep_waitidle).
-5.2 with first patch: "/f: update error: blocks 0 files 1"
     (because the truncation works accidentally, and softdep_waitidle()
     doesn't exist to detect the unflushable worklist item created by
     the reachable softdep_change_linkcnt() call).
   Panic later (due to broken worklist after a buggy remount is permitted?).
-current with first patch and kib's patch: "softdep_waitidle: ..."
     (because the bug fixed in the second patch is exposed again and
     softdep_waitidle() detects it).

Bruce


More information about the freebsd-fs mailing list