diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2011-05-31 14:26:07 +0300 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2011-06-03 18:12:31 +0300 |
commit | 837072377034d0a0b18b851d1ab95676b245cc0a (patch) | |
tree | 7020b369ac3373f292d59fe5fd470f73ecb69897 | |
parent | 812eb258311f89bcd664a34a620f249d54a2cd83 (diff) | |
download | linux-3.10-837072377034d0a0b18b851d1ab95676b245cc0a.tar.gz linux-3.10-837072377034d0a0b18b851d1ab95676b245cc0a.tar.bz2 linux-3.10-837072377034d0a0b18b851d1ab95676b245cc0a.zip |
UBIFS: fix clean znode counter corruption in error cases
UBIFS maintains per-filesystem and global clean znode counters
('c->clean_zn_cnt' and 'ubifs_clean_zn_cnt'). It is important to maintain
correct values there since the shrinker relies on 'ubifs_clean_zn_cnt'.
However, in case of failures during commit the counters were corrupted. E.g.,
if a failure happens in the middle of 'write_index()', then some nodes in the
commit list ('c->cnext') are marked as clean, and some are marked as dirty. And
the 'ubifs_destroy_tnc_subtree()' frees does not retrun correct count, and we
end up with non-zero 'c->clean_zn_cnt' when unmounting. This means that if we
have 2 file-sytem and one of them fails, and we unmount it,
'ubifs_clean_zn_cnt' stays incorrect and confuses the shrinker.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
-rw-r--r-- | fs/ubifs/tnc.c | 9 |
1 files changed, 5 insertions, 4 deletions
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index 8119b1fd8d9..91b4213dde8 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -2876,12 +2876,13 @@ static void tnc_destroy_cnext(struct ubifs_info *c) */ void ubifs_tnc_close(struct ubifs_info *c) { - long clean_freed; - tnc_destroy_cnext(c); if (c->zroot.znode) { - clean_freed = ubifs_destroy_tnc_subtree(c->zroot.znode); - atomic_long_sub(clean_freed, &ubifs_clean_zn_cnt); + long n; + + ubifs_destroy_tnc_subtree(c->zroot.znode); + n = atomic_long_read(&c->clean_zn_cnt); + atomic_long_sub(n, &ubifs_clean_zn_cnt); } kfree(c->gap_lebs); kfree(c->ilebs); |