if (err)
                goto out_ret;
 
+       /*
+        * On data journalling we skip straight to the transaction handle:
+        * there's no delalloc; page truncated will be checked later; the
+        * early return w/ all buffers mapped (calculates size/len) can't
+        * be used; and there's no dioread_nolock, so only ext4_get_block.
+        */
+       if (ext4_should_journal_data(inode))
+               goto retry_alloc;
+
        /* Delalloc case is easy... */
        if (test_opt(inode->i_sb, DELALLOC) &&
-           !ext4_should_journal_data(inode) &&
            !ext4_nonda_switch(inode->i_sb)) {
                do {
                        err = block_page_mkwrite(vma, vmf,
        /*
         * Return if we have all the buffers mapped. This avoids the need to do
         * journal_start/journal_stop which can block and take a long time
+        *
+        * This cannot be done for data journalling, as we have to add the
+        * inode to the transaction's list to writeprotect pages on commit.
         */
        if (page_has_buffers(page)) {
                if (!ext4_walk_page_buffers(NULL, page_buffers(page),
                ret = VM_FAULT_SIGBUS;
                goto out;
        }
-       err = block_page_mkwrite(vma, vmf, get_block);
-       if (!err && ext4_should_journal_data(inode)) {
-               if (ext4_walk_page_buffers(handle, page_buffers(page), 0,
-                         PAGE_SIZE, NULL, do_journal_get_write_access)) {
+       /*
+        * Data journalling can't use block_page_mkwrite() because it
+        * will set_buffer_dirty() before do_journal_get_write_access()
+        * thus might hit warning messages for dirty metadata buffers.
+        */
+       if (!ext4_should_journal_data(inode)) {
+               err = block_page_mkwrite(vma, vmf, get_block);
+       } else {
+               lock_page(page);
+               size = i_size_read(inode);
+               /* Page got truncated from under us? */
+               if (page->mapping != mapping || page_offset(page) > size) {
                        unlock_page(page);
-                       ret = VM_FAULT_SIGBUS;
+                       ret = VM_FAULT_NOPAGE;
                        ext4_journal_stop(handle);
                        goto out;
                }
-               ext4_set_inode_state(inode, EXT4_STATE_JDATA);
+
+               if (page->index == size >> PAGE_SHIFT)
+                       len = size & ~PAGE_MASK;
+               else
+                       len = PAGE_SIZE;
+
+               err = __block_write_begin(page, 0, len, ext4_get_block);
+               if (!err) {
+                       if (ext4_walk_page_buffers(handle, page_buffers(page),
+                                       0, len, NULL, do_journal_get_write_access)) {
+                               unlock_page(page);
+                               ret = VM_FAULT_SIGBUS;
+                               ext4_journal_stop(handle);
+                               goto out;
+                       }
+                       ext4_set_inode_state(inode, EXT4_STATE_JDATA);
+               } else {
+                       unlock_page(page);
+               }
        }
        ext4_journal_stop(handle);
        if (err == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))