]> git.ipfire.org Git - thirdparty/kernel/stable.git/blobdiff - fs/jbd2/journal.c
jbd2: fix race when writing superblock
[thirdparty/kernel/stable.git] / fs / jbd2 / journal.c
index 8ef6b6daaa7a641bc39f507b42ad89db488f98ce..67ac91b530508dec9fbb60514fbf218fd53cbb4a 100644 (file)
@@ -1356,6 +1356,10 @@ static int journal_reset(journal_t *journal)
        return jbd2_journal_start_thread(journal);
 }
 
+/*
+ * This function expects that the caller will have locked the journal
+ * buffer head, and will return with it unlocked
+ */
 static int jbd2_write_superblock(journal_t *journal, int write_flags)
 {
        struct buffer_head *bh = journal->j_sb_buffer;
@@ -1365,7 +1369,6 @@ static int jbd2_write_superblock(journal_t *journal, int write_flags)
        trace_jbd2_write_superblock(journal, write_flags);
        if (!(journal->j_flags & JBD2_BARRIER))
                write_flags &= ~(REQ_FUA | REQ_PREFLUSH);
-       lock_buffer(bh);
        if (buffer_write_io_error(bh)) {
                /*
                 * Oh, dear.  A previous attempt to write the journal
@@ -1424,6 +1427,7 @@ int jbd2_journal_update_sb_log_tail(journal_t *journal, tid_t tail_tid,
        jbd_debug(1, "JBD2: updating superblock (start %lu, seq %u)\n",
                  tail_block, tail_tid);
 
+       lock_buffer(journal->j_sb_buffer);
        sb->s_sequence = cpu_to_be32(tail_tid);
        sb->s_start    = cpu_to_be32(tail_block);
 
@@ -1454,18 +1458,17 @@ static void jbd2_mark_journal_empty(journal_t *journal, int write_op)
        journal_superblock_t *sb = journal->j_superblock;
 
        BUG_ON(!mutex_is_locked(&journal->j_checkpoint_mutex));
-       read_lock(&journal->j_state_lock);
-       /* Is it already empty? */
-       if (sb->s_start == 0) {
-               read_unlock(&journal->j_state_lock);
+       lock_buffer(journal->j_sb_buffer);
+       if (sb->s_start == 0) {         /* Is it already empty? */
+               unlock_buffer(journal->j_sb_buffer);
                return;
        }
+
        jbd_debug(1, "JBD2: Marking journal as empty (seq %d)\n",
                  journal->j_tail_sequence);
 
        sb->s_sequence = cpu_to_be32(journal->j_tail_sequence);
        sb->s_start    = cpu_to_be32(0);
-       read_unlock(&journal->j_state_lock);
 
        jbd2_write_superblock(journal, write_op);
 
@@ -1488,9 +1491,8 @@ void jbd2_journal_update_sb_errno(journal_t *journal)
        journal_superblock_t *sb = journal->j_superblock;
        int errcode;
 
-       read_lock(&journal->j_state_lock);
+       lock_buffer(journal->j_sb_buffer);
        errcode = journal->j_errno;
-       read_unlock(&journal->j_state_lock);
        if (errcode == -ESHUTDOWN)
                errcode = 0;
        jbd_debug(1, "JBD2: updating superblock error (errno %d)\n", errcode);
@@ -1894,28 +1896,27 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat,
 
        sb = journal->j_superblock;
 
+       /* Load the checksum driver if necessary */
+       if ((journal->j_chksum_driver == NULL) &&
+           INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V3)) {
+               journal->j_chksum_driver = crypto_alloc_shash("crc32c", 0, 0);
+               if (IS_ERR(journal->j_chksum_driver)) {
+                       printk(KERN_ERR "JBD2: Cannot load crc32c driver.\n");
+                       journal->j_chksum_driver = NULL;
+                       return 0;
+               }
+               /* Precompute checksum seed for all metadata */
+               journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid,
+                                                  sizeof(sb->s_uuid));
+       }
+
+       lock_buffer(journal->j_sb_buffer);
+
        /* If enabling v3 checksums, update superblock */
        if (INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V3)) {
                sb->s_checksum_type = JBD2_CRC32C_CHKSUM;
                sb->s_feature_compat &=
                        ~cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM);
-
-               /* Load the checksum driver */
-               if (journal->j_chksum_driver == NULL) {
-                       journal->j_chksum_driver = crypto_alloc_shash("crc32c",
-                                                                     0, 0);
-                       if (IS_ERR(journal->j_chksum_driver)) {
-                               printk(KERN_ERR "JBD2: Cannot load crc32c "
-                                      "driver.\n");
-                               journal->j_chksum_driver = NULL;
-                               return 0;
-                       }
-
-                       /* Precompute checksum seed for all metadata */
-                       journal->j_csum_seed = jbd2_chksum(journal, ~0,
-                                                          sb->s_uuid,
-                                                          sizeof(sb->s_uuid));
-               }
        }
 
        /* If enabling v1 checksums, downgrade superblock */
@@ -1927,6 +1928,7 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat,
        sb->s_feature_compat    |= cpu_to_be32(compat);
        sb->s_feature_ro_compat |= cpu_to_be32(ro);
        sb->s_feature_incompat  |= cpu_to_be32(incompat);
+       unlock_buffer(journal->j_sb_buffer);
 
        return 1;
 #undef COMPAT_FEATURE_ON
@@ -2067,7 +2069,7 @@ int jbd2_journal_wipe(journal_t *journal, int write)
        err = jbd2_journal_skip_recovery(journal);
        if (write) {
                /* Lock to make assertions happy... */
-               mutex_lock(&journal->j_checkpoint_mutex);
+               mutex_lock_io(&journal->j_checkpoint_mutex);
                jbd2_mark_journal_empty(journal, REQ_SYNC | REQ_FUA);
                mutex_unlock(&journal->j_checkpoint_mutex);
        }