linux/fs/ext4/ext4_jbd2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Interface between ext4 and JBD
   4 */
   5
   6#include "ext4_jbd2.h"
   7
   8#include <trace/events/ext4.h>
   9
  10/* Just increment the non-pointer handle value */
  11static handle_t *ext4_get_nojournal(void)
  12{
  13        handle_t *handle = current->journal_info;
  14        unsigned long ref_cnt = (unsigned long)handle;
  15
  16        BUG_ON(ref_cnt >= EXT4_NOJOURNAL_MAX_REF_COUNT);
  17
  18        ref_cnt++;
  19        handle = (handle_t *)ref_cnt;
  20
  21        current->journal_info = handle;
  22        return handle;
  23}
  24
  25
  26/* Decrement the non-pointer handle value */
  27static void ext4_put_nojournal(handle_t *handle)
  28{
  29        unsigned long ref_cnt = (unsigned long)handle;
  30
  31        BUG_ON(ref_cnt == 0);
  32
  33        ref_cnt--;
  34        handle = (handle_t *)ref_cnt;
  35
  36        current->journal_info = handle;
  37}
  38
  39/*
  40 * Wrappers for jbd2_journal_start/end.
  41 */
  42static int ext4_journal_check_start(struct super_block *sb)
  43{
  44        journal_t *journal;
  45
  46        might_sleep();
  47
  48        if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
  49                return -EIO;
  50
  51        if (sb_rdonly(sb))
  52                return -EROFS;
  53        WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);
  54        journal = EXT4_SB(sb)->s_journal;
  55        /*
  56         * Special case here: if the journal has aborted behind our
  57         * backs (eg. EIO in the commit thread), then we still need to
  58         * take the FS itself readonly cleanly.
  59         */
  60        if (journal && is_journal_aborted(journal)) {
  61                ext4_abort(sb, "Detected aborted journal");
  62                return -EROFS;
  63        }
  64        return 0;
  65}
  66
  67handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line,
  68                                  int type, int blocks, int rsv_blocks)
  69{
  70        journal_t *journal;
  71        int err;
  72
  73        trace_ext4_journal_start(sb, blocks, rsv_blocks, _RET_IP_);
  74        err = ext4_journal_check_start(sb);
  75        if (err < 0)
  76                return ERR_PTR(err);
  77
  78        journal = EXT4_SB(sb)->s_journal;
  79        if (!journal)
  80                return ext4_get_nojournal();
  81        return jbd2__journal_start(journal, blocks, rsv_blocks, GFP_NOFS,
  82                                   type, line);
  83}
  84
  85int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle)
  86{
  87        struct super_block *sb;
  88        int err;
  89        int rc;
  90
  91        if (!ext4_handle_valid(handle)) {
  92                ext4_put_nojournal(handle);
  93                return 0;
  94        }
  95
  96        err = handle->h_err;
  97        if (!handle->h_transaction) {
  98                rc = jbd2_journal_stop(handle);
  99                return err ? err : rc;
 100        }
 101
 102        sb = handle->h_transaction->t_journal->j_private;
 103        rc = jbd2_journal_stop(handle);
 104
 105        if (!err)
 106                err = rc;
 107        if (err)
 108                __ext4_std_error(sb, where, line, err);
 109        return err;
 110}
 111
 112handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line,
 113                                        int type)
 114{
 115        struct super_block *sb;
 116        int err;
 117
 118        if (!ext4_handle_valid(handle))
 119                return ext4_get_nojournal();
 120
 121        sb = handle->h_journal->j_private;
 122        trace_ext4_journal_start_reserved(sb, handle->h_buffer_credits,
 123                                          _RET_IP_);
 124        err = ext4_journal_check_start(sb);
 125        if (err < 0) {
 126                jbd2_journal_free_reserved(handle);
 127                return ERR_PTR(err);
 128        }
 129
 130        err = jbd2_journal_start_reserved(handle, type, line);
 131        if (err < 0)
 132                return ERR_PTR(err);
 133        return handle;
 134}
 135
 136static void ext4_journal_abort_handle(const char *caller, unsigned int line,
 137                                      const char *err_fn,
 138                                      struct buffer_head *bh,
 139                                      handle_t *handle, int err)
 140{
 141        char nbuf[16];
 142        const char *errstr = ext4_decode_error(NULL, err, nbuf);
 143
 144        BUG_ON(!ext4_handle_valid(handle));
 145
 146        if (bh)
 147                BUFFER_TRACE(bh, "abort");
 148
 149        if (!handle->h_err)
 150                handle->h_err = err;
 151
 152        if (is_handle_aborted(handle))
 153                return;
 154
 155        printk(KERN_ERR "EXT4-fs: %s:%d: aborting transaction: %s in %s\n",
 156               caller, line, errstr, err_fn);
 157
 158        jbd2_journal_abort_handle(handle);
 159}
 160
 161int __ext4_journal_get_write_access(const char *where, unsigned int line,
 162                                    handle_t *handle, struct buffer_head *bh)
 163{
 164        int err = 0;
 165
 166        might_sleep();
 167
 168        if (ext4_handle_valid(handle)) {
 169                err = jbd2_journal_get_write_access(handle, bh);
 170                if (err)
 171                        ext4_journal_abort_handle(where, line, __func__, bh,
 172                                                  handle, err);
 173        }
 174        return err;
 175}
 176
 177/*
 178 * The ext4 forget function must perform a revoke if we are freeing data
 179 * which has been journaled.  Metadata (eg. indirect blocks) must be
 180 * revoked in all cases.
 181 *
 182 * "bh" may be NULL: a metadata block may have been freed from memory
 183 * but there may still be a record of it in the journal, and that record
 184 * still needs to be revoked.
 185 *
 186 * If the handle isn't valid we're not journaling, but we still need to
 187 * call into ext4_journal_revoke() to put the buffer head.
 188 */
 189int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
 190                  int is_metadata, struct inode *inode,
 191                  struct buffer_head *bh, ext4_fsblk_t blocknr)
 192{
 193        int err;
 194
 195        might_sleep();
 196
 197        trace_ext4_forget(inode, is_metadata, blocknr);
 198        BUFFER_TRACE(bh, "enter");
 199
 200        jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, "
 201                  "data mode %x\n",
 202                  bh, is_metadata, inode->i_mode,
 203                  test_opt(inode->i_sb, DATA_FLAGS));
 204
 205        /* In the no journal case, we can just do a bforget and return */
 206        if (!ext4_handle_valid(handle)) {
 207                bforget(bh);
 208                return 0;
 209        }
 210
 211        /* Never use the revoke function if we are doing full data
 212         * journaling: there is no need to, and a V1 superblock won't
 213         * support it.  Otherwise, only skip the revoke on un-journaled
 214         * data blocks. */
 215
 216        if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
 217            (!is_metadata && !ext4_should_journal_data(inode))) {
 218                if (bh) {
 219                        BUFFER_TRACE(bh, "call jbd2_journal_forget");
 220                        err = jbd2_journal_forget(handle, bh);
 221                        if (err)
 222                                ext4_journal_abort_handle(where, line, __func__,
 223                                                          bh, handle, err);
 224                        return err;
 225                }
 226                return 0;
 227        }
 228
 229        /*
 230         * data!=journal && (is_metadata || should_journal_data(inode))
 231         */
 232        BUFFER_TRACE(bh, "call jbd2_journal_revoke");
 233        err = jbd2_journal_revoke(handle, blocknr, bh);
 234        if (err) {
 235                ext4_journal_abort_handle(where, line, __func__,
 236                                          bh, handle, err);
 237                __ext4_abort(inode->i_sb, where, line,
 238                           "error %d when attempting revoke", err);
 239        }
 240        BUFFER_TRACE(bh, "exit");
 241        return err;
 242}
 243
 244int __ext4_journal_get_create_access(const char *where, unsigned int line,
 245                                handle_t *handle, struct buffer_head *bh)
 246{
 247        int err = 0;
 248
 249        if (ext4_handle_valid(handle)) {
 250                err = jbd2_journal_get_create_access(handle, bh);
 251                if (err)
 252                        ext4_journal_abort_handle(where, line, __func__,
 253                                                  bh, handle, err);
 254        }
 255        return err;
 256}
 257
 258int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
 259                                 handle_t *handle, struct inode *inode,
 260                                 struct buffer_head *bh)
 261{
 262        int err = 0;
 263
 264        might_sleep();
 265
 266        set_buffer_meta(bh);
 267        set_buffer_prio(bh);
 268        if (ext4_handle_valid(handle)) {
 269                err = jbd2_journal_dirty_metadata(handle, bh);
 270                /* Errors can only happen due to aborted journal or a nasty bug */
 271                if (!is_handle_aborted(handle) && WARN_ON_ONCE(err)) {
 272                        ext4_journal_abort_handle(where, line, __func__, bh,
 273                                                  handle, err);
 274                        if (inode == NULL) {
 275                                pr_err("EXT4: jbd2_journal_dirty_metadata "
 276                                       "failed: handle type %u started at "
 277                                       "line %u, credits %u/%u, errcode %d",
 278                                       handle->h_type,
 279                                       handle->h_line_no,
 280                                       handle->h_requested_credits,
 281                                       handle->h_buffer_credits, err);
 282                                return err;
 283                        }
 284                        ext4_error_inode(inode, where, line,
 285                                         bh->b_blocknr,
 286                                         "journal_dirty_metadata failed: "
 287                                         "handle type %u started at line %u, "
 288                                         "credits %u/%u, errcode %d",
 289                                         handle->h_type,
 290                                         handle->h_line_no,
 291                                         handle->h_requested_credits,
 292                                         handle->h_buffer_credits, err);
 293                }
 294        } else {
 295                if (inode)
 296                        mark_buffer_dirty_inode(bh, inode);
 297                else
 298                        mark_buffer_dirty(bh);
 299                if (inode && inode_needs_sync(inode)) {
 300                        sync_dirty_buffer(bh);
 301                        if (buffer_req(bh) && !buffer_uptodate(bh)) {
 302                                struct ext4_super_block *es;
 303
 304                                es = EXT4_SB(inode->i_sb)->s_es;
 305                                es->s_last_error_block =
 306                                        cpu_to_le64(bh->b_blocknr);
 307                                ext4_error_inode(inode, where, line,
 308                                                 bh->b_blocknr,
 309                                        "IO error syncing itable block");
 310                                err = -EIO;
 311                        }
 312                }
 313        }
 314        return err;
 315}
 316
 317int __ext4_handle_dirty_super(const char *where, unsigned int line,
 318                              handle_t *handle, struct super_block *sb)
 319{
 320        struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
 321        int err = 0;
 322
 323        ext4_superblock_csum_set(sb);
 324        if (ext4_handle_valid(handle)) {
 325                err = jbd2_journal_dirty_metadata(handle, bh);
 326                if (err)
 327                        ext4_journal_abort_handle(where, line, __func__,
 328                                                  bh, handle, err);
 329        } else
 330                mark_buffer_dirty(bh);
 331        return err;
 332}
 333