]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/ext3/inode.c
[IA64] Fix rwsem: RWSEM_WAITING_BIAS must not be unsigned.
[karo-tx-linux.git] / fs / ext3 / inode.c
index cc55cecf9fbc9f7034572cc5b92d8735dfbbbeea..5e0faf4cda797800713a335f3c6d3d5d64fcd2c2 100644 (file)
@@ -1166,9 +1166,25 @@ static int walk_page_buffers(    handle_t *handle,
 static int do_journal_get_write_access(handle_t *handle,
                                        struct buffer_head *bh)
 {
+       int dirty = buffer_dirty(bh);
+       int ret;
+
        if (!buffer_mapped(bh) || buffer_freed(bh))
                return 0;
-       return ext3_journal_get_write_access(handle, bh);
+       /*
+        * __block_prepare_write() could have dirtied some buffers. Clean
+        * the dirty bit as jbd2_journal_get_write_access() could complain
+        * otherwise about fs integrity issues. Setting of the dirty bit
+        * by __block_prepare_write() isn't a real problem here as we clear
+        * the bit before releasing a page lock and thus writeback cannot
+        * ever write the buffer.
+        */
+       if (dirty)
+               clear_buffer_dirty(bh);
+       ret = ext3_journal_get_write_access(handle, bh);
+       if (!ret && dirty)
+               ret = ext3_journal_dirty_metadata(handle, bh);
+       return ret;
 }
 
 /*
@@ -1641,10 +1657,7 @@ static int ext3_writeback_writepage(struct page *page,
                goto out_fail;
        }
 
-       if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode))
-               ret = nobh_writepage(page, ext3_get_block, wbc);
-       else
-               ret = block_write_full_page(page, ext3_get_block, wbc);
+       ret = block_write_full_page(page, ext3_get_block, wbc);
 
        err = ext3_journal_stop(handle);
        if (!ret)
@@ -1949,17 +1962,6 @@ static int ext3_block_truncate_page(handle_t *handle, struct page *page,
        length = blocksize - (offset & (blocksize - 1));
        iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
 
-       /*
-        * For "nobh" option,  we can only work if we don't need to
-        * read-in the page - otherwise we create buffers to do the IO.
-        */
-       if (!page_has_buffers(page) && test_opt(inode->i_sb, NOBH) &&
-            ext3_should_writeback_data(inode) && PageUptodate(page)) {
-               zero_user(page, offset, length);
-               set_page_dirty(page);
-               goto unlock;
-       }
-
        if (!page_has_buffers(page))
                create_empty_buffers(page, blocksize, 0);
 
@@ -2310,27 +2312,6 @@ static void ext3_free_branches(handle_t *handle, struct inode *inode,
                                           (__le32*)bh->b_data + addr_per_block,
                                           depth);
 
-                       /*
-                        * We've probably journalled the indirect block several
-                        * times during the truncate.  But it's no longer
-                        * needed and we now drop it from the transaction via
-                        * journal_revoke().
-                        *
-                        * That's easy if it's exclusively part of this
-                        * transaction.  But if it's part of the committing
-                        * transaction then journal_forget() will simply
-                        * brelse() it.  That means that if the underlying
-                        * block is reallocated in ext3_get_block(),
-                        * unmap_underlying_metadata() will find this block
-                        * and will try to get rid of it.  damn, damn.
-                        *
-                        * If this block has already been committed to the
-                        * journal, a revoke record will be written.  And
-                        * revoke records must be emitted *before* clearing
-                        * this block's bit in the bitmaps.
-                        */
-                       ext3_forget(handle, 1, inode, bh, bh->b_blocknr);
-
                        /*
                         * Everything below this this pointer has been
                         * released.  Now let this top-of-subtree go.
@@ -2354,6 +2335,31 @@ static void ext3_free_branches(handle_t *handle, struct inode *inode,
                                truncate_restart_transaction(handle, inode);
                        }
 
+                       /*
+                        * We've probably journalled the indirect block several
+                        * times during the truncate.  But it's no longer
+                        * needed and we now drop it from the transaction via
+                        * journal_revoke().
+                        *
+                        * That's easy if it's exclusively part of this
+                        * transaction.  But if it's part of the committing
+                        * transaction then journal_forget() will simply
+                        * brelse() it.  That means that if the underlying
+                        * block is reallocated in ext3_get_block(),
+                        * unmap_underlying_metadata() will find this block
+                        * and will try to get rid of it.  damn, damn. Thus
+                        * we don't allow a block to be reallocated until
+                        * a transaction freeing it has fully committed.
+                        *
+                        * We also have to make sure journal replay after a
+                        * crash does not overwrite non-journaled data blocks
+                        * with old metadata when the block got reallocated for
+                        * data.  Thus we have to store a revoke record for a
+                        * block in the same transaction in which we free the
+                        * block.
+                        */
+                       ext3_forget(handle, 1, inode, bh, bh->b_blocknr);
+
                        ext3_free_blocks(handle, inode, nr, 1);
 
                        if (parent_bh) {