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, -journal->j_errno, "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                                  int revoke_creds)
  70{
  71        journal_t *journal;
  72        int err;
  73
  74        trace_ext4_journal_start(sb, blocks, rsv_blocks, revoke_creds,
  75                                 _RET_IP_);
  76        err = ext4_journal_check_start(sb);
  77        if (err < 0)
  78                return ERR_PTR(err);
  79
  80        journal = EXT4_SB(sb)->s_journal;
  81        if (!journal)
  82                return ext4_get_nojournal();
  83        return jbd2__journal_start(journal, blocks, rsv_blocks, revoke_creds,
  84                                   GFP_NOFS, type, line);
  85}
  86
  87int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle)
  88{
  89        struct super_block *sb;
  90        int err;
  91        int rc;
  92
  93        if (!ext4_handle_valid(handle)) {
  94                ext4_put_nojournal(handle);
  95                return 0;
  96        }
  97
  98        err = handle->h_err;
  99        if (!handle->h_transaction) {
 100                rc = jbd2_journal_stop(handle);
 101                return err ? err : rc;
 102        }
 103
 104        sb = handle->h_transaction->t_journal->j_private;
 105        rc = jbd2_journal_stop(handle);
 106
 107        if (!err)
 108                err = rc;
 109        if (err)
 110                __ext4_std_error(sb, where, line, err);
 111        return err;
 112}
 113
 114handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line,
 115                                        int type)
 116{
 117        struct super_block *sb;
 118        int err;
 119
 120        if (!ext4_handle_valid(handle))
 121                return ext4_get_nojournal();
 122
 123        sb = handle->h_journal->j_private;
 124        trace_ext4_journal_start_reserved(sb,
 125                                jbd2_handle_buffer_credits(handle), _RET_IP_);
 126        err = ext4_journal_check_start(sb);
 127        if (err < 0) {
 128                jbd2_journal_free_reserved(handle);
 129                return ERR_PTR(err);
 130        }
 131
 132        err = jbd2_journal_start_reserved(handle, type, line);
 133        if (err < 0)
 134                return ERR_PTR(err);
 135        return handle;
 136}
 137
 138int __ext4_journal_ensure_credits(handle_t *handle, int check_cred,
 139                                  int extend_cred, int revoke_cred)
 140{
 141        if (!ext4_handle_valid(handle))
 142                return 0;
 143        if (is_handle_aborted(handle))
 144                return -EROFS;
 145        if (jbd2_handle_buffer_credits(handle) >= check_cred &&
 146            handle->h_revoke_credits >= revoke_cred)
 147                return 0;
 148        extend_cred = max(0, extend_cred - jbd2_handle_buffer_credits(handle));
 149        revoke_cred = max(0, revoke_cred - handle->h_revoke_credits);
 150        return ext4_journal_extend(handle, extend_cred, revoke_cred);
 151}
 152
 153static void ext4_journal_abort_handle(const char *caller, unsigned int line,
 154                                      const char *err_fn,
 155                                      struct buffer_head *bh,
 156                                      handle_t *handle, int err)
 157{
 158        char nbuf[16];
 159        const char *errstr = ext4_decode_error(NULL, err, nbuf);
 160
 161        BUG_ON(!ext4_handle_valid(handle));
 162
 163        if (bh)
 164                BUFFER_TRACE(bh, "abort");
 165
 166        if (!handle->h_err)
 167                handle->h_err = err;
 168
 169        if (is_handle_aborted(handle))
 170                return;
 171
 172        printk(KERN_ERR "EXT4-fs: %s:%d: aborting transaction: %s in %s\n",
 173               caller, line, errstr, err_fn);
 174
 175        jbd2_journal_abort_handle(handle);
 176}
 177
 178int __ext4_journal_get_write_access(const char *where, unsigned int line,
 179                                    handle_t *handle, struct buffer_head *bh)
 180{
 181        int err = 0;
 182
 183        might_sleep();
 184
 185        if (ext4_handle_valid(handle)) {
 186                err = jbd2_journal_get_write_access(handle, bh);
 187                if (err)
 188                        ext4_journal_abort_handle(where, line, __func__, bh,
 189                                                  handle, err);
 190        }
 191        return err;
 192}
 193
 194/*
 195 * The ext4 forget function must perform a revoke if we are freeing data
 196 * which has been journaled.  Metadata (eg. indirect blocks) must be
 197 * revoked in all cases.
 198 *
 199 * "bh" may be NULL: a metadata block may have been freed from memory
 200 * but there may still be a record of it in the journal, and that record
 201 * still needs to be revoked.
 202 *
 203 * If the handle isn't valid we're not journaling, but we still need to
 204 * call into ext4_journal_revoke() to put the buffer head.
 205 */
 206int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
 207                  int is_metadata, struct inode *inode,
 208                  struct buffer_head *bh, ext4_fsblk_t blocknr)
 209{
 210        int err;
 211
 212        might_sleep();
 213
 214        trace_ext4_forget(inode, is_metadata, blocknr);
 215        BUFFER_TRACE(bh, "enter");
 216
 217        jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, "
 218                  "data mode %x\n",
 219                  bh, is_metadata, inode->i_mode,
 220                  test_opt(inode->i_sb, DATA_FLAGS));
 221
 222        /* In the no journal case, we can just do a bforget and return */
 223        if (!ext4_handle_valid(handle)) {
 224                bforget(bh);
 225                return 0;
 226        }
 227
 228        /* Never use the revoke function if we are doing full data
 229         * journaling: there is no need to, and a V1 superblock won't
 230         * support it.  Otherwise, only skip the revoke on un-journaled
 231         * data blocks. */
 232
 233        if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
 234            (!is_metadata && !ext4_should_journal_data(inode))) {
 235                if (bh) {
 236                        BUFFER_TRACE(bh, "call jbd2_journal_forget");
 237                        err = jbd2_journal_forget(handle, bh);
 238                        if (err)
 239                                ext4_journal_abort_handle(where, line, __func__,
 240                                                          bh, handle, err);
 241                        return err;
 242                }
 243                return 0;
 244        }
 245
 246        /*
 247         * data!=journal && (is_metadata || should_journal_data(inode))
 248         */
 249        BUFFER_TRACE(bh, "call jbd2_journal_revoke");
 250        err = jbd2_journal_revoke(handle, blocknr, bh);
 251        if (err) {
 252                ext4_journal_abort_handle(where, line, __func__,
 253                                          bh, handle, err);
 254                __ext4_abort(inode->i_sb, where, line, -err,
 255                           "error %d when attempting revoke", err);
 256        }
 257        BUFFER_TRACE(bh, "exit");
 258        return err;
 259}
 260
 261int __ext4_journal_get_create_access(const char *where, unsigned int line,
 262                                handle_t *handle, struct buffer_head *bh)
 263{
 264        int err = 0;
 265
 266        if (ext4_handle_valid(handle)) {
 267                err = jbd2_journal_get_create_access(handle, bh);
 268                if (err)
 269                        ext4_journal_abort_handle(where, line, __func__,
 270                                                  bh, handle, err);
 271        }
 272        return err;
 273}
 274
 275int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
 276                                 handle_t *handle, struct inode *inode,
 277                                 struct buffer_head *bh)
 278{
 279        int err = 0;
 280
 281        might_sleep();
 282
 283        set_buffer_meta(bh);
 284        set_buffer_prio(bh);
 285        if (ext4_handle_valid(handle)) {
 286                err = jbd2_journal_dirty_metadata(handle, bh);
 287                /* Errors can only happen due to aborted journal or a nasty bug */
 288                if (!is_handle_aborted(handle) && WARN_ON_ONCE(err)) {
 289                        ext4_journal_abort_handle(where, line, __func__, bh,
 290                                                  handle, err);
 291                        if (inode == NULL) {
 292                                pr_err("EXT4: jbd2_journal_dirty_metadata "
 293                                       "failed: handle type %u started at "
 294                                       "line %u, credits %u/%u, errcode %d",
 295                                       handle->h_type,
 296                                       handle->h_line_no,
 297                                       handle->h_requested_credits,
 298                                       jbd2_handle_buffer_credits(handle), err);
 299                                return err;
 300                        }
 301                        ext4_error_inode(inode, where, line,
 302                                         bh->b_blocknr,
 303                                         "journal_dirty_metadata failed: "
 304                                         "handle type %u started at line %u, "
 305                                         "credits %u/%u, errcode %d",
 306                                         handle->h_type,
 307                                         handle->h_line_no,
 308                                         handle->h_requested_credits,
 309                                         jbd2_handle_buffer_credits(handle),
 310                                         err);
 311                }
 312        } else {
 313                set_buffer_uptodate(bh);
 314                if (inode)
 315                        mark_buffer_dirty_inode(bh, inode);
 316                else
 317                        mark_buffer_dirty(bh);
 318                if (inode && inode_needs_sync(inode)) {
 319                        sync_dirty_buffer(bh);
 320                        if (buffer_req(bh) && !buffer_uptodate(bh)) {
 321                                ext4_error_inode_err(inode, where, line,
 322                                                     bh->b_blocknr, EIO,
 323                                        "IO error syncing itable block");
 324                                err = -EIO;
 325                        }
 326                }
 327        }
 328        return err;
 329}
 330
 331int __ext4_handle_dirty_super(const char *where, unsigned int line,
 332                              handle_t *handle, struct super_block *sb)
 333{
 334        struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
 335        int err = 0;
 336
 337        ext4_superblock_csum_set(sb);
 338        if (ext4_handle_valid(handle)) {
 339                err = jbd2_journal_dirty_metadata(handle, bh);
 340                if (err)
 341                        ext4_journal_abort_handle(where, line, __func__,
 342                                                  bh, handle, err);
 343        } else
 344                mark_buffer_dirty(bh);
 345        return err;
 346}
 347