📄 transaction.c
字号:
result = 0; jbd_debug(3, "extended handle %p by %d\n", handle, nblocks); error_out: unlock_journal (journal); return result;}/* * journal_restart: restart a handle for a multi-transaction filesystem * operation. * * If the journal_extend() call above fails to grant new buffer credits * to a running handle, a call to journal_restart will commit the * handle's transaction so far and reattach the handle to a new * transaction capabable of guaranteeing the requested number of * credits. */int journal_restart(handle_t *handle, int nblocks){ transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; int ret; /* If we've had an abort of any type, don't even think about * actually doing the restart! */ if (is_handle_aborted(handle)) return 0; /* First unlink the handle from its current transaction, and * start the commit on that. */ J_ASSERT (transaction->t_updates > 0); J_ASSERT (journal_current_handle() == handle); transaction->t_outstanding_credits -= handle->h_buffer_credits; transaction->t_updates--; if (!transaction->t_updates) wake_up(&journal->j_wait_updates); jbd_debug(2, "restarting handle %p\n", handle); log_start_commit(journal, transaction); handle->h_buffer_credits = nblocks; ret = start_this_handle(journal, handle); return ret;}/* * Barrier operation: establish a transaction barrier. * * This locks out any further updates from being started, and blocks * until all existing updates have completed, returning only once the * journal is in a quiescent state with no updates running. * * The journal lock should not be held on entry. */void journal_lock_updates (journal_t *journal){ lock_journal(journal); ++journal->j_barrier_count; /* Wait until there are no running updates */ while (1) { transaction_t *transaction = journal->j_running_transaction; if (!transaction) break; if (!transaction->t_updates) break; unlock_journal(journal); sleep_on(&journal->j_wait_updates); lock_journal(journal); } unlock_journal(journal); /* We have now established a barrier against other normal * updates, but we also need to barrier against other * journal_lock_updates() calls to make sure that we serialise * special journal-locked operations too. */ down(&journal->j_barrier);}/* * Release a transaction barrier obtained with journal_lock_updates(). * * Should be called without the journal lock held. */void journal_unlock_updates (journal_t *journal){ lock_journal(journal); J_ASSERT (journal->j_barrier_count != 0); up(&journal->j_barrier); --journal->j_barrier_count; wake_up(&journal->j_wait_transaction_locked); unlock_journal(journal);}/* * journal_get_write_access: notify intent to modify a buffer for metadata * (not data) update. * * If the buffer is already part of the current transaction, then there * is nothing we need to do. If it is already part of a prior * transaction which we are still committing to disk, then we need to * make sure that we do not overwrite the old copy: we do copy-out to * preserve the copy going to disk. We also account the buffer against * the handle's metadata buffer credits (unless the buffer is already * part of the transaction, that is). * * Returns an error code or 0 on success. * * In full data journalling mode the buffer may be of type BJ_AsyncData, * because we're write()ing a buffer which is also part of a shared mapping. */static intdo_get_write_access(handle_t *handle, struct journal_head *jh, int force_copy) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; int error; char *frozen_buffer = NULL; int need_copy = 0; jbd_debug(5, "buffer_head %p, force_copy %d\n", jh, force_copy); JBUFFER_TRACE(jh, "entry");repeat: /* @@@ Need to check for errors here at some point. */ /* * AKPM: neither bdflush nor kupdate run with the BKL. There's * nothing we can do to prevent them from starting writeout of a * BUF_DIRTY buffer at any time. And checkpointing buffers are on * BUF_DIRTY. So. We no longer assert that the buffer is unlocked. * * However. It is very wrong for us to allow ext3 to start directly * altering the ->b_data of buffers which may at that very time be * undergoing writeout to the client filesystem. This can leave * the filesystem in an inconsistent, transient state if we crash. * So what we do is to steal the buffer if it is in checkpoint * mode and dirty. The journal lock will keep out checkpoint-mode * state transitions within journal_remove_checkpoint() and the buffer * is locked to keep bdflush/kupdate/whoever away from it as well. * * AKPM: we have replaced all the lock_journal_bh_wait() stuff with a * simple lock_journal(). This code here will care for locked buffers. */ /* * The buffer_locked() || buffer_dirty() tests here are simply an * optimisation tweak. If anyone else in the system decides to * lock this buffer later on, we'll blow up. There doesn't seem * to be a good reason why they should do this. */ if (jh->b_cp_transaction && (buffer_locked(jh2bh(jh)) || buffer_dirty(jh2bh(jh)))) { unlock_journal(journal); lock_buffer(jh2bh(jh)); spin_lock(&journal_datalist_lock); if (jh->b_cp_transaction && buffer_dirty(jh2bh(jh))) { /* OK, we need to steal it */ JBUFFER_TRACE(jh, "stealing from checkpoint mode"); J_ASSERT_JH(jh, jh->b_next_transaction == NULL); J_ASSERT_JH(jh, jh->b_frozen_data == NULL); J_ASSERT(handle->h_buffer_credits > 0); handle->h_buffer_credits--; /* This will clear BH_Dirty and set BH_JBDDirty. */ JBUFFER_TRACE(jh, "file as BJ_Reserved"); __journal_file_buffer(jh, transaction, BJ_Reserved); /* And pull it off BUF_DIRTY, onto BUF_CLEAN */ refile_buffer(jh2bh(jh)); /* * The buffer is now hidden from bdflush. It is * metadata against the current transaction. */ JBUFFER_TRACE(jh, "steal from cp mode is complete"); } spin_unlock(&journal_datalist_lock); unlock_buffer(jh2bh(jh)); lock_journal(journal); goto repeat; } J_ASSERT_JH(jh, !buffer_locked(jh2bh(jh))); error = -EROFS; if (is_handle_aborted(handle)) goto out_unlocked; error = 0; spin_lock(&journal_datalist_lock); /* The buffer is already part of this transaction if * b_transaction or b_next_transaction points to it. */ if (jh->b_transaction == transaction || jh->b_next_transaction == transaction) goto done_locked; /* If there is already a copy-out version of this buffer, then * we don't need to make another one. */ if (jh->b_frozen_data) { JBUFFER_TRACE(jh, "has frozen data"); J_ASSERT_JH(jh, jh->b_next_transaction == NULL); jh->b_next_transaction = transaction; J_ASSERT_JH(jh, handle->h_buffer_credits > 0); handle->h_buffer_credits--; goto done_locked; } /* Is there data here we need to preserve? */ if (jh->b_transaction && jh->b_transaction != transaction) { JBUFFER_TRACE(jh, "owned by older transaction"); J_ASSERT_JH(jh, jh->b_next_transaction == NULL); J_ASSERT_JH(jh, jh->b_transaction == journal->j_committing_transaction); /* There is one case we have to be very careful about. * If the committing transaction is currently writing * this buffer out to disk and has NOT made a copy-out, * then we cannot modify the buffer contents at all * right now. The essence of copy-out is that it is the * extra copy, not the primary copy, which gets * journaled. If the primary copy is already going to * disk then we cannot do copy-out here. */ if (jh->b_jlist == BJ_Shadow) { JBUFFER_TRACE(jh, "on shadow: sleep"); spin_unlock(&journal_datalist_lock); unlock_journal(journal); /* commit wakes up all shadow buffers after IO */ sleep_on(&jh2bh(jh)->b_wait); lock_journal(journal); goto repeat; } /* Only do the copy if the currently-owning transaction * still needs it. If it is on the Forget list, the * committing transaction is past that stage. The * buffer had better remain locked during the kmalloc, * but that should be true --- we hold the journal lock * still and the buffer is already on the BUF_JOURNAL * list so won't be flushed. * * Subtle point, though: if this is a get_undo_access, * then we will be relying on the frozen_data to contain * the new value of the committed_data record after the * transaction, so we HAVE to force the frozen_data copy * in that case. */ if (jh->b_jlist != BJ_Forget || force_copy) { JBUFFER_TRACE(jh, "generate frozen data"); if (!frozen_buffer) { JBUFFER_TRACE(jh, "allocate memory for buffer"); spin_unlock(&journal_datalist_lock); unlock_journal(journal); frozen_buffer = jbd_kmalloc(jh2bh(jh)->b_size, GFP_NOFS); lock_journal(journal); if (!frozen_buffer) { printk(KERN_EMERG __FUNCTION__ "OOM for frozen_buffer\n"); JBUFFER_TRACE(jh, "oom!"); error = -ENOMEM; spin_lock(&journal_datalist_lock); goto done_locked; } goto repeat; } jh->b_frozen_data = frozen_buffer; frozen_buffer = NULL; need_copy = 1; } jh->b_next_transaction = transaction; } J_ASSERT(handle->h_buffer_credits > 0); handle->h_buffer_credits--; /* Finally, if the buffer is not journaled right now, we need to * make sure it doesn't get written to disk before the caller * actually commits the new data. */ if (!jh->b_transaction) { JBUFFER_TRACE(jh, "no transaction"); J_ASSERT_JH(jh, !jh->b_next_transaction); jh->b_transaction = transaction; JBUFFER_TRACE(jh, "file as BJ_Reserved"); __journal_file_buffer(jh, transaction, BJ_Reserved); } done_locked: spin_unlock(&journal_datalist_lock); if (need_copy) { struct page *page; int offset; char *source; J_ASSERT_JH(jh, buffer_uptodate(jh2bh(jh))); page = jh2bh(jh)->b_page; offset = ((unsigned long) jh2bh(jh)->b_data) & ~PAGE_MASK; source = kmap(page); memcpy(jh->b_frozen_data, source+offset, jh2bh(jh)->b_size); kunmap(page); } /* If we are about to journal a buffer, then any revoke pending on it is no longer valid. */ journal_cancel_revoke(handle, jh);out_unlocked: if (frozen_buffer) kfree(frozen_buffer); JBUFFER_TRACE(jh, "exit"); return error;}int journal_get_write_access (handle_t *handle, struct buffer_head *bh) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; struct journal_head *jh = journal_add_journal_head(bh); int rc; /* We do not want to get caught playing with fields which the * log thread also manipulates. Make sure that the buffer * completes any outstanding IO before proceeding. */ lock_journal(journal); rc = do_get_write_access(handle, jh, 0); journal_unlock_journal_head(jh); unlock_journal(journal); return rc;}/* * When the user wants to journal a newly created buffer_head * (ie. getblk() returned a new buffer and we are going to populate it * manually rather than reading off disk), then we need to keep the * buffer_head locked until it has been completely filled with new * data. In this case, we should be able to make the assertion that * the bh is not already part of an existing transaction. * * The buffer should already be locked by the caller by this point. * There is no lock ranking violation: it was a newly created, * unlocked buffer beforehand. */int journal_get_create_access (handle_t *handle, struct buffer_head *bh) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; struct journal_head *jh = journal_add_journal_head(bh); int err; jbd_debug(5, "journal_head %p\n", jh); lock_journal(journal); err = -EROFS; if (is_handle_aborted(handle)) goto out; err = 0; JBUFFER_TRACE(jh, "entry"); /* The buffer may already belong to this transaction due to * pre-zeroing in the filesystem's new_block code. It may also * be on the previous, committing transaction's lists, but it * HAS to be in Forget state in that case: the transaction must * have deleted the buffer for it to be reused here. */ J_ASSERT_JH(jh, (jh->b_transaction == transaction || jh->b_transaction == NULL || (jh->b_transaction == journal->j_committing_transaction && jh->b_jlist == BJ_Forget))); J_ASSERT_JH(jh, jh->b_next_transaction == NULL); J_ASSERT_JH(jh, buffer_locked(jh2bh(jh))); J_ASSERT_JH(jh, handle->h_buffer_credits > 0); handle->h_buffer_credits--; spin_lock(&journal_datalist_lock); if (jh->b_transaction == NULL) { jh->b_transaction = transaction; JBUFFER_TRACE(jh, "file as BJ_Reserved"); __journal_file_buffer(jh, transaction, BJ_Reserved); JBUFFER_TRACE(jh, "refile"); refile_buffer(jh2bh(jh)); } else if (jh->b_transaction == journal->j_committing_transaction) { JBUFFER_TRACE(jh, "set next transaction"); jh->b_next_transaction = transaction; } spin_unlock(&journal_datalist_lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -