// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
*/
/** * gfs2_struct2blk - compute stuff * @sdp: the filesystem * @nstruct: the number of structures * * Compute the number of log descriptor blocks needed to hold a certain number * of structures of a certain size. * * Returns: the number of blocks needed (minimum is always 1)
*/
/** * gfs2_remove_from_ail - Remove an entry from the ail lists, updating counters * @bd: The gfs2_bufdata to remove * * The ail lock _must_ be held when calling this function *
*/
/** * gfs2_ail1_start_one - Start I/O on a transaction * @sdp: The superblock * @wbc: The writeback control structure * @tr: The transaction to start I/O on * @plug: The block plug currently active
*/
if (!buffer_busy(bh)) { if (buffer_uptodate(bh)) {
list_move(&bd->bd_ail_st_list,
&tr->tr_ail2_list); continue;
} if (!cmpxchg(&sdp->sd_log_error, 0, -EIO)) {
gfs2_io_error_bh(sdp, bh);
gfs2_withdraw_delayed(sdp);
}
}
if (gfs2_withdrawing_or_withdrawn(sdp)) {
gfs2_remove_from_ail(bd); continue;
} if (!buffer_dirty(bh)) continue; if (gl == bd->bd_gl) continue;
gl = bd->bd_gl;
list_move(&bd->bd_ail_st_list, &tr->tr_ail1_list);
mapping = bh->b_folio->mapping; if (!mapping) continue;
spin_unlock(&sdp->sd_ail_lock);
BUG_ON(GFS2_SB(mapping->host) != sdp); if (gfs2_is_jdata(GFS2_I(mapping->host)))
ret = gfs2_jdata_writeback(mapping, wbc); else
ret = mapping->a_ops->writepages(mapping, wbc); if (need_resched()) {
blk_finish_plug(plug);
cond_resched();
blk_start_plug(plug);
}
spin_lock(&sdp->sd_ail_lock); if (ret == -ENODATA) /* if a jdata write into a new hole */
ret = 0; /* ignore it */
mapping_set_error(mapping, ret); if (ret || wbc->nr_to_write <= 0) break; return -EBUSY;
}
/** * gfs2_ail1_flush - start writeback of some ail1 entries * @sdp: The super block * @wbc: The writeback control structure * * Writes back some ail1 entries, according to the limits in the * writeback control structure
*/
/** * gfs2_ail1_empty_one - Check whether or not a trans in the AIL has been synced * @sdp: the filesystem * @tr: the transaction * @max_revokes: If nonzero, issue revokes for the bd items for written buffers * * returns: the transaction's count of remaining active items
*/
list_for_each_entry_safe_reverse(bd, s, &tr->tr_ail1_list,
bd_ail_st_list) {
bh = bd->bd_bh;
gfs2_assert(sdp, bd->bd_tr == tr); /* * If another process flagged an io error, e.g. writing to the * journal, error all other bhs and move them off the ail1 to * prevent a tight loop when unmount tries to flush ail1, * regardless of whether they're still busy. If no outside * errors were found and the buffer is busy, move to the next. * If the ail buffer is not busy and caught an error, flag it * for others.
*/ if (!sdp->sd_log_error && buffer_busy(bh)) {
active_count++; continue;
} if (!buffer_uptodate(bh) &&
!cmpxchg(&sdp->sd_log_error, 0, -EIO)) {
gfs2_io_error_bh(sdp, bh);
gfs2_withdraw_delayed(sdp);
} /* * If we have space for revokes and the bd is no longer on any * buf list, we can just add a revoke for it immediately and * avoid having to put it on the ail2 list, where it would need * to be revoked later.
*/ if (*max_revokes && list_empty(&bd->bd_list)) {
gfs2_add_revoke(sdp, bd);
(*max_revokes)--; continue;
}
list_move(&bd->bd_ail_st_list, &tr->tr_ail2_list);
} return active_count;
}
/** * gfs2_ail1_empty - Try to empty the ail1 lists * @sdp: The superblock * @max_revokes: If non-zero, add revokes where appropriate * * Tries to empty the ail1 lists, starting with the oldest first. * Returns %true if the ail1 list is now empty.
*/
staticbool gfs2_ail1_empty(struct gfs2_sbd *sdp, int max_revokes)
{ struct gfs2_trans *tr, *s; int oldest_tr = 1; bool empty;
available = atomic_read(&sdp->sd_log_revokes_available); while (available >= revokes) { if (atomic_try_cmpxchg(&sdp->sd_log_revokes_available,
&available, available - revokes)) returntrue;
} returnfalse;
}
/** * gfs2_log_release_revokes - Release a given number of revokes * @sdp: The GFS2 superblock * @revokes: The number of revokes to release * * sdp->sd_log_flush_lock must be held.
*/ void gfs2_log_release_revokes(struct gfs2_sbd *sdp, unsignedint revokes)
{ if (revokes)
atomic_add(revokes, &sdp->sd_log_revokes_available);
}
/** * gfs2_log_release - Release a given number of log blocks * @sdp: The GFS2 superblock * @blks: The number of blocks *
*/
/** * __gfs2_log_try_reserve - Try to make a log reservation * @sdp: The GFS2 superblock * @blks: The number of blocks to reserve * @taboo_blks: The number of blocks to leave free * * Try to do the same as __gfs2_log_reserve(), but fail if no more log * space is immediately available.
*/ staticbool __gfs2_log_try_reserve(struct gfs2_sbd *sdp, unsignedint blks, unsignedint taboo_blks)
{ unsigned wanted = blks + taboo_blks; unsignedint free_blocks;
/** * __gfs2_log_reserve - Make a log reservation * @sdp: The GFS2 superblock * @blks: The number of blocks to reserve * @taboo_blks: The number of blocks to leave free * * @taboo_blks is set to 0 for logd, and to GFS2_LOG_FLUSH_MIN_BLOCKS * for all other processes. This ensures that when the log is almost full, * logd will still be able to call gfs2_log_flush one more time without * blocking, which will advance the tail and make some more log space * available. * * We no longer flush the log here, instead we wake up logd to do that * for us. To avoid the thundering herd and to ensure that we deal fairly * with queued waiters, we use an exclusive wait. This means that when we * get woken with enough journal space to get our reservation, we need to * wake the next waiter on the list.
*/
atomic_add(blks, &sdp->sd_log_blks_needed); for (;;) { if (current != sdp->sd_logd_process)
wake_up(&sdp->sd_logd_waitq);
io_wait_event(sdp->sd_log_waitq,
(free_blocks = atomic_read(&sdp->sd_log_blks_free),
free_blocks >= wanted)); do { if (atomic_try_cmpxchg(&sdp->sd_log_blks_free,
&free_blocks,
free_blocks - blks)) goto reserved;
} while (free_blocks >= wanted);
}
reserved:
trace_gfs2_log_blocks(sdp, -blks); if (atomic_sub_return(blks, &sdp->sd_log_blks_needed))
wake_up(&sdp->sd_log_waitq);
}
/** * gfs2_log_try_reserve - Try to make a log reservation * @sdp: The GFS2 superblock * @tr: The transaction * @extra_revokes: The number of additional revokes reserved (output) * * This is similar to gfs2_log_reserve, but sdp->sd_log_flush_lock must be * held for correct revoke accounting.
*/
*extra_revokes = 0; if (revokes && !__gfs2_log_try_reserve_revokes(sdp, revokes)) {
revoke_blks = DIV_ROUND_UP(revokes, sdp->sd_inptrs);
*extra_revokes = revoke_blks * sdp->sd_inptrs - revokes;
blks += revoke_blks;
} if (!blks) returntrue; if (__gfs2_log_try_reserve(sdp, blks, GFS2_LOG_FLUSH_MIN_BLOCKS)) returntrue; if (!revoke_blks)
gfs2_log_release_revokes(sdp, revokes); returnfalse;
}
/** * gfs2_log_reserve - Make a log reservation * @sdp: The GFS2 superblock * @tr: The transaction * @extra_revokes: The number of additional revokes reserved (output) * * sdp->sd_log_flush_lock must not be held.
*/
/** * log_distance - Compute distance between two journal blocks * @sdp: The GFS2 superblock * @newer: The most recent journal block of the pair * @older: The older journal block of the pair * * Compute the distance (in the journal direction) between two * blocks in the journal * * Returns: the distance in blocks
*/
/** * calc_reserved - Calculate the number of blocks to keep reserved * @sdp: The GFS2 superblock * * This is complex. We need to reserve room for all our currently used * metadata blocks (e.g. normal file I/O rewriting file time stamps) and * all our journaled data blocks for journaled files (e.g. files in the * meta_fs like rindex, or files for which chattr +j was done.) * If we don't reserve enough space, corruption will follow. * * We can have metadata blocks and jdata blocks in the same journal. Each * type gets its own log descriptor, for which we need to reserve a block. * In fact, each type has the potential for needing more than one log descriptor * in cases where we have more blocks than will fit in a log descriptor. * Metadata journal entries take up half the space of journaled buffer entries. * * Also, we need to reserve blocks for revoke journal entries and one for an * overall header for the lot. * * Returns: the number of blocks reserved
*/ staticunsignedint calc_reserved(struct gfs2_sbd *sdp)
{ unsignedint reserved = GFS2_LOG_FLUSH_MIN_BLOCKS; unsignedint blocks; struct gfs2_trans *tr = sdp->sd_log_tr;
/** * gfs2_flush_revokes - Add as many revokes to the system transaction as we can * @sdp: The GFS2 superblock * * Our usual strategy is to defer writing revokes as much as we can in the hope * that we'll eventually overwrite the journal, which will make those revokes * go away. This changes when we flush the log: at that point, there will * likely be some left-over space in the last revoke block of that transaction. * We can fill that space with additional revokes for blocks that have already * been written back. This will basically come at no cost now, and will save * us from having to keep track of those blocks on the AIL2 list later.
*/ void gfs2_flush_revokes(struct gfs2_sbd *sdp)
{ /* number of revokes we still have room for */ unsignedint max_revokes = atomic_read(&sdp->sd_log_revokes_available);
/** * gfs2_write_log_header - Write a journal log header buffer at lblock * @sdp: The GFS2 superblock * @jd: journal descriptor of the journal to which we are writing * @seq: sequence number * @tail: tail of the log * @lblock: value for lh_blkno (block number relative to start of journal) * @flags: log header flags GFS2_LOG_HEAD_* * @op_flags: flags to pass to the bio * * Returns: the initialized log buffer descriptor
*/
ktime_get_coarse_real_ts64(&tv);
lh->lh_nsec = cpu_to_be32(tv.tv_nsec);
lh->lh_sec = cpu_to_be64(tv.tv_sec); if (!list_empty(&jd->extent_list))
dblock = gfs2_log_bmap(jd, lblock); else { unsignedint extlen; int ret;
extlen = 1;
ret = gfs2_get_extent(jd->jd_inode, lblock, &dblock, &extlen); if (gfs2_assert_withdraw(sdp, ret == 0)) return;
}
lh->lh_addr = cpu_to_be64(dblock);
lh->lh_jinode = cpu_to_be64(GFS2_I(jd->jd_inode)->i_no_addr);
/* We may only write local statfs, quota, etc., when writing to our own journal. The values are left 0 when recovering a journal
different from our own. */ if (!(flags & GFS2_LOG_HEAD_RECOVERY)) {
lh->lh_statfs_addr =
cpu_to_be64(GFS2_I(sdp->sd_sc_inode)->i_no_addr);
lh->lh_quota_addr =
cpu_to_be64(GFS2_I(sdp->sd_qc_inode)->i_no_addr);
/** * log_write_header - Get and initialize a journal header buffer * @sdp: The GFS2 superblock * @flags: The log header flags, including log header origin * * Returns: the initialized log buffer descriptor
*/
/** * gfs2_ail_drain - drain the ail lists after a withdraw * @sdp: Pointer to GFS2 superblock
*/ void gfs2_ail_drain(struct gfs2_sbd *sdp)
{ struct gfs2_trans *tr;
spin_lock(&sdp->sd_ail_lock); /* * For transactions on the sd_ail1_list we need to drain both the * ail1 and ail2 lists. That's because function gfs2_ail1_start_one * (temporarily) moves items from its tr_ail1 list to tr_ail2 list * before revokes are sent for that block. Items on the sd_ail2_list * should have already gotten beyond that point, so no need.
*/ while (!list_empty(&sdp->sd_ail1_list)) {
tr = list_first_entry(&sdp->sd_ail1_list, struct gfs2_trans,
tr_list);
gfs2_ail_empty_tr(sdp, tr, &tr->tr_ail1_list);
gfs2_ail_empty_tr(sdp, tr, &tr->tr_ail2_list);
list_del(&tr->tr_list);
gfs2_trans_free(sdp, tr);
} while (!list_empty(&sdp->sd_ail2_list)) {
tr = list_first_entry(&sdp->sd_ail2_list, struct gfs2_trans,
tr_list);
gfs2_ail_empty_tr(sdp, tr, &tr->tr_ail2_list);
list_del(&tr->tr_list);
gfs2_trans_free(sdp, tr);
}
gfs2_drain_revokes(sdp);
spin_unlock(&sdp->sd_ail_lock);
}
/** * empty_ail1_list - try to start IO and empty the ail1 list * @sdp: Pointer to GFS2 superblock
*/ staticvoid empty_ail1_list(struct gfs2_sbd *sdp)
{ unsignedlong start = jiffies; bool empty = false;
while (!empty) { if (time_after(jiffies, start + (HZ * 600))) {
fs_err(sdp, "Error: In %s for 10 minutes! t=%d\n",
__func__, current->journal_info ? 1 : 0);
dump_ail_list(sdp); return;
}
gfs2_ail1_start(sdp);
gfs2_ail1_wait(sdp);
empty = gfs2_ail1_empty(sdp, 0);
if (gfs2_withdrawing_or_withdrawn(sdp)) break;
}
if (gfs2_withdrawing(sdp))
gfs2_withdraw(sdp);
}
/** * trans_drain - drain the buf and databuf queue for a failed transaction * @tr: the transaction to drain * * When this is called, we're taking an error exit for a log write that failed * but since we bypassed the after_commit functions, we need to remove the * items from the buf and databuf queue.
*/ staticvoid trans_drain(struct gfs2_trans *tr)
{ struct gfs2_bufdata *bd; struct list_head *head;
if (!tr) return;
head = &tr->tr_buf; while (!list_empty(head)) {
bd = list_first_entry(head, struct gfs2_bufdata, bd_list);
list_del_init(&bd->bd_list); if (!list_empty(&bd->bd_ail_st_list))
gfs2_remove_from_ail(bd);
kmem_cache_free(gfs2_bufdata_cachep, bd);
}
head = &tr->tr_databuf; while (!list_empty(head)) {
bd = list_first_entry(head, struct gfs2_bufdata, bd_list);
list_del_init(&bd->bd_list); if (!list_empty(&bd->bd_ail_st_list))
gfs2_remove_from_ail(bd);
kmem_cache_free(gfs2_bufdata_cachep, bd);
}
}
/** * gfs2_log_flush - flush incore transaction(s) * @sdp: The filesystem * @gl: The glock structure to flush. If NULL, flush the whole incore log * @flags: The log header flags: GFS2_LOG_HEAD_FLUSH_* and debug flags *
*/
repeat: /* * Do this check while holding the log_flush_lock to prevent new * buffers from being added to the ail via gfs2_pin()
*/ if (gfs2_withdrawing_or_withdrawn(sdp) ||
!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) goto out;
/* Log might have been flushed while we waited for the flush lock */ if (gl && !test_bit(GLF_LFLUSH, &gl->gl_flags)) goto out;
if (flags & GFS2_LOG_HEAD_FLUSH_SHUTDOWN)
clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
if (unlikely(frozen)) if (gfs2_assert_withdraw_delayed(sdp, !reserved_revokes)) goto out_withdraw;
gfs2_ordered_write(sdp); if (gfs2_withdrawing_or_withdrawn(sdp)) goto out_withdraw;
lops_before_commit(sdp, tr); if (gfs2_withdrawing_or_withdrawn(sdp)) goto out_withdraw; if (sdp->sd_jdesc)
gfs2_log_submit_bio(&sdp->sd_jdesc->jd_log_bio, REQ_OP_WRITE); if (gfs2_withdrawing_or_withdrawn(sdp)) goto out_withdraw;
out_withdraw:
trans_drain(tr); /** * If the tr_list is empty, we're withdrawing during a log * flush that targets a transaction, but the transaction was * never queued onto any of the ail lists. Here we add it to * ail1 just so that ail_drain() will find and free it.
*/
spin_lock(&sdp->sd_ail_lock); if (tr && list_empty(&tr->tr_list))
list_add(&tr->tr_list, &sdp->sd_ail1_list);
spin_unlock(&sdp->sd_ail_lock);
tr = NULL; goto out_end;
}
/** * gfs2_merge_trans - Merge a new transaction into a cached transaction * @sdp: the filesystem * @new: New transaction to be merged
*/
/** * gfs2_log_commit - Commit a transaction to the log * @sdp: the filesystem * @tr: the transaction * * We wake up gfs2_logd if the number of pinned blocks exceed thresh1 * or the total number of used blocks (pinned blocks plus AIL blocks) * is greater than thresh2. * * At mount time thresh1 is 2/5ths of journal size, thresh2 is 4/5ths of * journal size. * * Returns: errno
*/
/** * gfs2_logd - Update log tail as Active Items get flushed to in-place blocks * @data: Pointer to GFS2 superblock * * Also, periodically check to make sure that we're using the most recent * journal index.
*/
int gfs2_logd(void *data)
{ struct gfs2_sbd *sdp = data; unsignedlong t = 1;
set_freezable(); while (!kthread_should_stop()) { if (gfs2_withdrawing_or_withdrawn(sdp)) break;
/* Check for errors writing to the journal */ if (sdp->sd_log_error) {
gfs2_lm(sdp, "GFS2: fsid=%s: error %d: " "withdrawing the file system to " "prevent further damage.\n",
sdp->sd_fsname, sdp->sd_log_error);
gfs2_withdraw(sdp); break;
}
if (gfs2_jrnl_flush_reqd(sdp) || t == 0) {
gfs2_ail1_empty(sdp, 0);
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_LOGD_JFLUSH_REQD);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.