[PATCH] resierfs: fix reiserfs_invalidatepage race against data=ordered
[powerpc.git] / fs / reiserfs / journal.c
index a8e29e9..bc8fe96 100644 (file)
@@ -152,18 +152,16 @@ static struct reiserfs_bitmap_node *allocate_bitmap_node(struct super_block
        struct reiserfs_bitmap_node *bn;
        static int id;
 
-       bn = reiserfs_kmalloc(sizeof(struct reiserfs_bitmap_node), GFP_NOFS,
-                             p_s_sb);
+       bn = kmalloc(sizeof(struct reiserfs_bitmap_node), GFP_NOFS);
        if (!bn) {
                return NULL;
        }
-       bn->data = reiserfs_kmalloc(p_s_sb->s_blocksize, GFP_NOFS, p_s_sb);
+       bn->data = kzalloc(p_s_sb->s_blocksize, GFP_NOFS);
        if (!bn->data) {
-               reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb);
+               kfree(bn);
                return NULL;
        }
        bn->id = id++;
-       memset(bn->data, 0, p_s_sb->s_blocksize);
        INIT_LIST_HEAD(&bn->list);
        return bn;
 }
@@ -197,8 +195,8 @@ static inline void free_bitmap_node(struct super_block *p_s_sb,
        struct reiserfs_journal *journal = SB_JOURNAL(p_s_sb);
        journal->j_used_bitmap_nodes--;
        if (journal->j_free_bitmap_nodes > REISERFS_MAX_BITMAP_NODES) {
-               reiserfs_kfree(bn->data, p_s_sb->s_blocksize, p_s_sb);
-               reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb);
+               kfree(bn->data);
+               kfree(bn);
        } else {
                list_add(&bn->list, &journal->j_bitmap_nodes);
                journal->j_free_bitmap_nodes++;
@@ -276,8 +274,8 @@ static int free_bitmap_nodes(struct super_block *p_s_sb)
        while (next != &journal->j_bitmap_nodes) {
                bn = list_entry(next, struct reiserfs_bitmap_node, list);
                list_del(next);
-               reiserfs_kfree(bn->data, p_s_sb->s_blocksize, p_s_sb);
-               reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb);
+               kfree(bn->data);
+               kfree(bn);
                next = journal->j_bitmap_nodes.next;
                journal->j_free_bitmap_nodes--;
        }
@@ -581,7 +579,7 @@ static inline void put_journal_list(struct super_block *s,
                               jl->j_trans_id, jl->j_refcount);
        }
        if (--jl->j_refcount == 0)
-               reiserfs_kfree(jl, sizeof(struct reiserfs_journal_list), s);
+               kfree(jl);
 }
 
 /*
@@ -879,6 +877,19 @@ static int write_ordered_buffers(spinlock_t * lock,
                if (!buffer_uptodate(bh)) {
                        ret = -EIO;
                }
+               /* ugly interaction with invalidatepage here.
+                * reiserfs_invalidate_page will pin any buffer that has a valid
+                * journal head from an older transaction.  If someone else sets
+                * our buffer dirty after we write it in the first loop, and
+                * then someone truncates the page away, nobody will ever write
+                * the buffer. We're safe if we write the page one last time
+                * after freeing the journal header.
+                */
+               if (buffer_dirty(bh) && unlikely(bh->b_page->mapping == NULL)) {
+                       spin_unlock(lock);
+                       ll_rw_block(WRITE, 1, &bh);
+                       spin_lock(lock);
+               }
                put_bh(bh);
                cond_resched_lock(lock);
        }
@@ -1039,6 +1050,10 @@ static int flush_commit_list(struct super_block *s,
        }
        atomic_dec(&journal->j_async_throttle);
 
+       /* We're skipping the commit if there's an error */
+       if (retval || reiserfs_is_journal_aborted(journal))
+               barrier = 0;
+
        /* wait on everything written so far before writing the commit
         * if we are in barrier mode, send the commit down now
         */
@@ -1077,10 +1092,16 @@ static int flush_commit_list(struct super_block *s,
        BUG_ON(atomic_read(&(jl->j_commit_left)) != 1);
 
        if (!barrier) {
-               if (buffer_dirty(jl->j_commit_bh))
-                       BUG();
-               mark_buffer_dirty(jl->j_commit_bh);
-               sync_dirty_buffer(jl->j_commit_bh);
+               /* If there was a write error in the journal - we can't commit
+                * this transaction - it will be invalid and, if successful,
+                * will just end up propogating the write error out to
+                * the file system. */
+               if (likely(!retval && !reiserfs_is_journal_aborted (journal))) {
+                       if (buffer_dirty(jl->j_commit_bh))
+                               BUG();
+                       mark_buffer_dirty(jl->j_commit_bh) ;
+                       sync_dirty_buffer(jl->j_commit_bh) ;
+               }
        } else
                wait_on_buffer(jl->j_commit_bh);
 
@@ -1808,8 +1829,7 @@ void remove_journal_hash(struct super_block *sb,
 static void free_journal_ram(struct super_block *p_s_sb)
 {
        struct reiserfs_journal *journal = SB_JOURNAL(p_s_sb);
-       reiserfs_kfree(journal->j_current_jl,
-                      sizeof(struct reiserfs_journal_list), p_s_sb);
+       kfree(journal->j_current_jl);
        journal->j_num_lists--;
 
        vfree(journal->j_cnode_free_orig);
@@ -2083,21 +2103,15 @@ static int journal_read_transaction(struct super_block *p_s_sb,
        }
        trans_id = get_desc_trans_id(desc);
        /* now we know we've got a good transaction, and it was inside the valid time ranges */
-       log_blocks =
-           reiserfs_kmalloc(get_desc_trans_len(desc) *
-                            sizeof(struct buffer_head *), GFP_NOFS, p_s_sb);
-       real_blocks =
-           reiserfs_kmalloc(get_desc_trans_len(desc) *
-                            sizeof(struct buffer_head *), GFP_NOFS, p_s_sb);
+       log_blocks = kmalloc(get_desc_trans_len(desc) *
+                            sizeof(struct buffer_head *), GFP_NOFS);
+       real_blocks = kmalloc(get_desc_trans_len(desc) *
+                             sizeof(struct buffer_head *), GFP_NOFS);
        if (!log_blocks || !real_blocks) {
                brelse(c_bh);
                brelse(d_bh);
-               reiserfs_kfree(log_blocks,
-                              get_desc_trans_len(desc) *
-                              sizeof(struct buffer_head *), p_s_sb);
-               reiserfs_kfree(real_blocks,
-                              get_desc_trans_len(desc) *
-                              sizeof(struct buffer_head *), p_s_sb);
+               kfree(log_blocks);
+               kfree(real_blocks);
                reiserfs_warning(p_s_sb,
                                 "journal-1169: kmalloc failed, unable to mount FS");
                return -1;
@@ -2135,12 +2149,8 @@ static int journal_read_transaction(struct super_block *p_s_sb,
                        brelse_array(real_blocks, i);
                        brelse(c_bh);
                        brelse(d_bh);
-                       reiserfs_kfree(log_blocks,
-                                      get_desc_trans_len(desc) *
-                                      sizeof(struct buffer_head *), p_s_sb);
-                       reiserfs_kfree(real_blocks,
-                                      get_desc_trans_len(desc) *
-                                      sizeof(struct buffer_head *), p_s_sb);
+                       kfree(log_blocks);
+                       kfree(real_blocks);
                        return -1;
                }
        }
@@ -2156,12 +2166,8 @@ static int journal_read_transaction(struct super_block *p_s_sb,
                        brelse_array(real_blocks, get_desc_trans_len(desc));
                        brelse(c_bh);
                        brelse(d_bh);
-                       reiserfs_kfree(log_blocks,
-                                      get_desc_trans_len(desc) *
-                                      sizeof(struct buffer_head *), p_s_sb);
-                       reiserfs_kfree(real_blocks,
-                                      get_desc_trans_len(desc) *
-                                      sizeof(struct buffer_head *), p_s_sb);
+                       kfree(log_blocks);
+                       kfree(real_blocks);
                        return -1;
                }
                memcpy(real_blocks[i]->b_data, log_blocks[i]->b_data,
@@ -2183,12 +2189,8 @@ static int journal_read_transaction(struct super_block *p_s_sb,
                                     get_desc_trans_len(desc) - i);
                        brelse(c_bh);
                        brelse(d_bh);
-                       reiserfs_kfree(log_blocks,
-                                      get_desc_trans_len(desc) *
-                                      sizeof(struct buffer_head *), p_s_sb);
-                       reiserfs_kfree(real_blocks,
-                                      get_desc_trans_len(desc) *
-                                      sizeof(struct buffer_head *), p_s_sb);
+                       kfree(log_blocks);
+                       kfree(real_blocks);
                        return -1;
                }
                brelse(real_blocks[i]);
@@ -2207,12 +2209,8 @@ static int journal_read_transaction(struct super_block *p_s_sb,
        journal->j_trans_id = trans_id + 1;
        brelse(c_bh);
        brelse(d_bh);
-       reiserfs_kfree(log_blocks,
-                      le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *),
-                      p_s_sb);
-       reiserfs_kfree(real_blocks,
-                      le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *),
-                      p_s_sb);
+       kfree(log_blocks);
+       kfree(real_blocks);
        return 0;
 }
 
@@ -2461,14 +2459,8 @@ static int journal_read(struct super_block *p_s_sb)
 static struct reiserfs_journal_list *alloc_journal_list(struct super_block *s)
 {
        struct reiserfs_journal_list *jl;
-      retry:
-       jl = reiserfs_kmalloc(sizeof(struct reiserfs_journal_list), GFP_NOFS,
-                             s);
-       if (!jl) {
-               yield();
-               goto retry;
-       }
-       memset(jl, 0, sizeof(*jl));
+       jl = kzalloc(sizeof(struct reiserfs_journal_list),
+                    GFP_NOFS | __GFP_NOFAIL);
        INIT_LIST_HEAD(&jl->j_list);
        INIT_LIST_HEAD(&jl->j_working_list);
        INIT_LIST_HEAD(&jl->j_tail_bh_list);
@@ -2757,6 +2749,15 @@ int journal_init(struct super_block *p_s_sb, const char *j_dev_name,
        journal->j_cnode_used = 0;
        journal->j_must_wait = 0;
 
+       if (journal->j_cnode_free == 0) {
+               reiserfs_warning(p_s_sb, "journal-2004: Journal cnode memory "
+                                "allocation failed (%ld bytes). Journal is "
+                                "too large for available memory. Usually "
+                                "this is due to a journal that is too large.",
+                                sizeof (struct reiserfs_journal_cnode) * num_cnodes);
+               goto free_and_return;
+       }
+
        init_journal_hash(p_s_sb);
        jl = journal->j_current_jl;
        jl->j_list_bitmap = get_list_bitmap(p_s_sb, jl);
@@ -2868,8 +2869,7 @@ static void let_transaction_grow(struct super_block *sb, unsigned long trans_id)
        struct reiserfs_journal *journal = SB_JOURNAL(sb);
        unsigned long bcount = journal->j_bcount;
        while (1) {
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               schedule_timeout(1);
+               schedule_timeout_uninterruptible(1);
                journal->j_current_jl->j_state |= LIST_COMMIT_PENDING;
                while ((atomic_read(&journal->j_wcount) > 0 ||
                        atomic_read(&journal->j_jlock)) &&
@@ -3024,14 +3024,12 @@ struct reiserfs_transaction_handle *reiserfs_persistent_transaction(struct
                }
                return th;
        }
-       th = reiserfs_kmalloc(sizeof(struct reiserfs_transaction_handle),
-                             GFP_NOFS, s);
+       th = kmalloc(sizeof(struct reiserfs_transaction_handle), GFP_NOFS);
        if (!th)
                return NULL;
        ret = journal_begin(th, s, nblocks);
        if (ret) {
-               reiserfs_kfree(th, sizeof(struct reiserfs_transaction_handle),
-                              s);
+               kfree(th);
                return NULL;
        }
 
@@ -3049,8 +3047,7 @@ int reiserfs_end_persistent_transaction(struct reiserfs_transaction_handle *th)
                ret = -EIO;
        if (th->t_refcount == 0) {
                SB_JOURNAL(s)->j_persistent_trans--;
-               reiserfs_kfree(th, sizeof(struct reiserfs_transaction_handle),
-                              s);
+               kfree(th);
        }
        return ret;
 }
@@ -3907,10 +3904,13 @@ static int do_journal_end(struct reiserfs_transaction_handle *th,
                flush = 1;
        }
 #ifdef REISERFS_PREALLOCATE
-       /* quota ops might need to nest, setup the journal_info pointer for them */
+       /* quota ops might need to nest, setup the journal_info pointer for them
+        * and raise the refcount so that it is > 0. */
        current->journal_info = th;
+       th->t_refcount++;
        reiserfs_discard_all_prealloc(th);      /* it should not involve new blocks into
                                                 * the transaction */
+       th->t_refcount--;
        current->journal_info = th->t_handle_save;
 #endif