diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-08 13:16:07 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-08 13:16:07 -0700 |
commit | ef75bd71c5d31dc17ae41ff8bec92630a3037d69 (patch) | |
tree | b05e8843488668244df16c03e0fa6ccbf1d61980 /fs/gfs2 | |
parent | 78d9affbb0e79d48fd82b34ef9cd673a7c86d6f2 (diff) | |
parent | f4686c26ecc34e8e458b8235f0af5198c9b13bfd (diff) |
Merge tag 'gfs2-for-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2
Pull GFS2 updates from Andreas Gruenbacher:
"We've got the following patches ready for this merge window:
- "gfs2: Fix loop in gfs2_rbm_find (v2)"
A rework of a fix we ended up reverting in 5.0 because of an
iozone performance regression.
- "gfs2: read journal in large chunks"
"gfs2: fix race between gfs2_freeze_func and unmount"
An improved version of a commit we also ended up reverting in 5.0
because of a regression in xfstest generic/311. It turns out that
the journal changes were mostly innocent and that unfreeze didn't
wait for the freeze to complete, which caused the filesystem to be
unmounted before it was actually idle.
- "gfs2: Fix occasional glock use-after-free"
"gfs2: Fix iomap write page reclaim deadlock"
"gfs2: Fix lru_count going negative"
Fixes for various problems reported and partially fixed by Citrix
engineers. Thank you very much.
- "gfs2: clean_journal improperly set sd_log_flush_head"
Another fix from Bob.
- .. and a few other minor cleanups"
* tag 'gfs2-for-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
gfs2: read journal in large chunks
gfs2: Fix iomap write page reclaim deadlock
gfs2: fix race between gfs2_freeze_func and unmount
gfs2: Rename gfs2_trans_{add_unrevoke => remove_revoke}
gfs2: Rename sd_log_le_{revoke,ordered}
gfs2: Remove unnecessary extern declarations
gfs2: Remove misleading comments in gfs2_evict_inode
gfs2: Replace gl_revokes with a GLF flag
gfs2: Fix occasional glock use-after-free
gfs2: clean_journal improperly set sd_log_flush_head
gfs2: Fix lru_count going negative
gfs2: Fix loop in gfs2_rbm_find (v2)
Diffstat (limited to 'fs/gfs2')
-rw-r--r-- | fs/gfs2/aops.c | 14 | ||||
-rw-r--r-- | fs/gfs2/bmap.c | 118 | ||||
-rw-r--r-- | fs/gfs2/bmap.h | 1 | ||||
-rw-r--r-- | fs/gfs2/dir.c | 2 | ||||
-rw-r--r-- | fs/gfs2/glock.c | 25 | ||||
-rw-r--r-- | fs/gfs2/glops.c | 3 | ||||
-rw-r--r-- | fs/gfs2/incore.h | 9 | ||||
-rw-r--r-- | fs/gfs2/log.c | 47 | ||||
-rw-r--r-- | fs/gfs2/log.h | 5 | ||||
-rw-r--r-- | fs/gfs2/lops.c | 260 | ||||
-rw-r--r-- | fs/gfs2/lops.h | 11 | ||||
-rw-r--r-- | fs/gfs2/main.c | 1 | ||||
-rw-r--r-- | fs/gfs2/ops_fstype.c | 7 | ||||
-rw-r--r-- | fs/gfs2/recovery.c | 135 | ||||
-rw-r--r-- | fs/gfs2/recovery.h | 4 | ||||
-rw-r--r-- | fs/gfs2/rgrp.c | 56 | ||||
-rw-r--r-- | fs/gfs2/super.c | 20 | ||||
-rw-r--r-- | fs/gfs2/trans.c | 4 | ||||
-rw-r--r-- | fs/gfs2/trans.h | 2 | ||||
-rw-r--r-- | fs/gfs2/xattr.c | 6 |
20 files changed, 437 insertions, 293 deletions
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 05dd78f4b2b3..6210d4429d84 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -649,7 +649,7 @@ out_uninit: */ void adjust_fs_space(struct inode *inode) { - struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; + struct gfs2_sbd *sdp = GFS2_SB(inode); struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); struct gfs2_inode *l_ip = GFS2_I(sdp->sd_sc_inode); struct gfs2_statfs_change_host *m_sc = &sdp->sd_statfs_master; @@ -657,10 +657,13 @@ void adjust_fs_space(struct inode *inode) struct buffer_head *m_bh, *l_bh; u64 fs_total, new_free; + if (gfs2_trans_begin(sdp, 2 * RES_STATFS, 0) != 0) + return; + /* Total up the file system space, according to the latest rindex. */ fs_total = gfs2_ri_total(sdp); if (gfs2_meta_inode_buffer(m_ip, &m_bh) != 0) - return; + goto out; spin_lock(&sdp->sd_statfs_spin); gfs2_statfs_change_in(m_sc, m_bh->b_data + @@ -675,11 +678,14 @@ void adjust_fs_space(struct inode *inode) gfs2_statfs_change(sdp, new_free, new_free, 0); if (gfs2_meta_inode_buffer(l_ip, &l_bh) != 0) - goto out; + goto out2; update_statfs(sdp, m_bh, l_bh); brelse(l_bh); -out: +out2: brelse(m_bh); +out: + sdp->sd_rindex_uptodate = 0; + gfs2_trans_end(sdp); } /** diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 2f9290f69610..f42718dd292f 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -142,7 +142,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page) if (error) goto out_brelse; if (isdir) { - gfs2_trans_add_unrevoke(GFS2_SB(&ip->i_inode), block, 1); + gfs2_trans_remove_revoke(GFS2_SB(&ip->i_inode), block, 1); error = gfs2_dir_get_new_buffer(ip, block, &bh); if (error) goto out_brelse; @@ -676,7 +676,7 @@ static int gfs2_iomap_alloc(struct inode *inode, struct iomap *iomap, goto out; alloced += n; if (state != ALLOC_DATA || gfs2_is_jdata(ip)) - gfs2_trans_add_unrevoke(sdp, bn, n); + gfs2_trans_remove_revoke(sdp, bn, n); switch (state) { /* Growing height of tree */ case ALLOC_GROW_HEIGHT: @@ -925,6 +925,32 @@ do_alloc: goto out; } +/** + * gfs2_lblk_to_dblk - convert logical block to disk block + * @inode: the inode of the file we're mapping + * @lblock: the block relative to the start of the file + * @dblock: the returned dblock, if no error + * + * This function maps a single block from a file logical block (relative to + * the start of the file) to a file system absolute block using iomap. + * + * Returns: the absolute file system block, or an error + */ +int gfs2_lblk_to_dblk(struct inode *inode, u32 lblock, u64 *dblock) +{ + struct iomap iomap = { }; + struct metapath mp = { .mp_aheight = 1, }; + loff_t pos = (loff_t)lblock << inode->i_blkbits; + int ret; + + ret = gfs2_iomap_get(inode, pos, i_blocksize(inode), 0, &iomap, &mp); + release_metapath(&mp); + if (ret == 0) + *dblock = iomap.addr >> inode->i_blkbits; + + return ret; +} + static int gfs2_write_lock(struct inode *inode) { struct gfs2_inode *ip = GFS2_I(inode); @@ -965,17 +991,28 @@ static void gfs2_write_unlock(struct inode *inode) gfs2_glock_dq_uninit(&ip->i_gh); } +static int gfs2_iomap_page_prepare(struct inode *inode, loff_t pos, + unsigned len, struct iomap *iomap) +{ + struct gfs2_sbd *sdp = GFS2_SB(inode); + + return gfs2_trans_begin(sdp, RES_DINODE + (len >> inode->i_blkbits), 0); +} + static void gfs2_iomap_page_done(struct inode *inode, loff_t pos, unsigned copied, struct page *page, struct iomap *iomap) { struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); - if (page) + if (page && !gfs2_is_stuffed(ip)) gfs2_page_add_databufs(ip, page, offset_in_page(pos), copied); + gfs2_trans_end(sdp); } static const struct iomap_page_ops gfs2_iomap_page_ops = { + .page_prepare = gfs2_iomap_page_prepare, .page_done = gfs2_iomap_page_done, }; @@ -1031,31 +1068,45 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, if (alloc_required) rblocks += gfs2_rg_blocks(ip, data_blocks + ind_blocks); - ret = gfs2_trans_begin(sdp, rblocks, iomap->length >> inode->i_blkbits); - if (ret) - goto out_trans_fail; + if (unstuff || iomap->type == IOMAP_HOLE) { + struct gfs2_trans *tr; - if (unstuff) { - ret = gfs2_unstuff_dinode(ip, NULL); - if (ret) - goto out_trans_end; - release_metapath(mp); - ret = gfs2_iomap_get(inode, iomap->offset, iomap->length, - flags, iomap, mp); + ret = gfs2_trans_begin(sdp, rblocks, + iomap->length >> inode->i_blkbits); if (ret) - goto out_trans_end; - } + goto out_trans_fail; - if (iomap->type == IOMAP_HOLE) { - ret = gfs2_iomap_alloc(inode, iomap, flags, mp); - if (ret) { - gfs2_trans_end(sdp); - gfs2_inplace_release(ip); - punch_hole(ip, iomap->offset, iomap->length); - goto out_qunlock; + if (unstuff) { + ret = gfs2_unstuff_dinode(ip, NULL); + if (ret) + goto out_trans_end; + release_metapath(mp); + ret = gfs2_iomap_get(inode, iomap->offset, + iomap->length, flags, iomap, mp); + if (ret) + goto out_trans_end; + } + + if (iomap->type == IOMAP_HOLE) { + ret = gfs2_iomap_alloc(inode, iomap, flags, mp); + if (ret) { + gfs2_trans_end(sdp); + gfs2_inplace_release(ip); + punch_hole(ip, iomap->offset, iomap->length); + goto out_qunlock; + } } + + tr = current->journal_info; + if (tr->tr_num_buf_new) + __mark_inode_dirty(inode, I_DIRTY_DATASYNC); + else + gfs2_trans_add_meta(ip->i_gl, mp->mp_bh[0]); + + gfs2_trans_end(sdp); } - if (!gfs2_is_stuffed(ip) && gfs2_is_jdata(ip)) + + if (gfs2_is_stuffed(ip) || gfs2_is_jdata(ip)) iomap->page_ops = &gfs2_iomap_page_ops; return 0; @@ -1095,10 +1146,6 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, iomap->type != IOMAP_MAPPED) ret = -ENOTBLK; } - if (!ret) { - get_bh(mp.mp_bh[0]); - iomap->private = mp.mp_bh[0]; - } release_metapath(&mp); trace_gfs2_iomap_end(ip, iomap, ret); return ret; @@ -1109,27 +1156,16 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); - struct gfs2_trans *tr = current->journal_info; - struct buffer_head *dibh = iomap->private; if ((flags & (IOMAP_WRITE | IOMAP_DIRECT)) != IOMAP_WRITE) goto out; - if (iomap->type != IOMAP_INLINE) { + if (!gfs2_is_stuffed(ip)) gfs2_ordered_add_inode(ip); - if (tr->tr_num_buf_new) - __mark_inode_dirty(inode, I_DIRTY_DATASYNC); - else - gfs2_trans_add_meta(ip->i_gl, dibh); - } - - if (inode == sdp->sd_rindex) { + if (inode == sdp->sd_rindex) adjust_fs_space(inode); - sdp->sd_rindex_uptodate = 0; - } - gfs2_trans_end(sdp); gfs2_inplace_release(ip); if (length != written && (iomap->flags & IOMAP_F_NEW)) { @@ -1149,8 +1185,6 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, gfs2_write_unlock(inode); out: - if (dibh) - brelse(dibh); return 0; } diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h index 6b18fb323f0a..19a1fd772c61 100644 --- a/fs/gfs2/bmap.h +++ b/fs/gfs2/bmap.h @@ -64,5 +64,6 @@ extern int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd); extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd); extern int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length); +extern int gfs2_lblk_to_dblk(struct inode *inode, u32 lblock, u64 *dblock); #endif /* __BMAP_DOT_H__ */ diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index daa14ab4e31b..db9a05244a35 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -883,7 +883,7 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh, if (!bh) return NULL; - gfs2_trans_add_unrevoke(GFS2_SB(inode), bn, 1); + gfs2_trans_remove_revoke(GFS2_SB(inode), bn, 1); gfs2_trans_add_meta(ip->i_gl, bh); gfs2_metatype_set(bh, GFS2_METATYPE_LF, GFS2_FORMAT_LF); leaf = (struct gfs2_leaf *)bh->b_data; diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index d32964cd1117..15c605cfcfc8 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -140,6 +140,7 @@ void gfs2_glock_free(struct gfs2_glock *gl) { struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + BUG_ON(test_bit(GLF_REVOKES, &gl->gl_flags)); rhashtable_remove_fast(&gl_hash_table, &gl->gl_node, ht_parms); smp_mb(); wake_up_glock(gl); @@ -183,15 +184,19 @@ static int demote_ok(const struct gfs2_glock *gl) void gfs2_glock_add_to_lru(struct gfs2_glock *gl) { + if (!(gl->gl_ops->go_flags & GLOF_LRU)) + return; + spin_lock(&lru_lock); - if (!list_empty(&gl->gl_lru)) - list_del_init(&gl->gl_lru); - else + list_del(&gl->gl_lru); + list_add_tail(&gl->gl_lru, &lru_list); + + if (!test_bit(GLF_LRU, &gl->gl_flags)) { + set_bit(GLF_LRU, &gl->gl_flags); atomic_inc(&lru_count); + } - list_add_tail(&gl->gl_lru, &lru_list); - set_bit(GLF_LRU, &gl->gl_flags); spin_unlock(&lru_lock); } @@ -201,7 +206,7 @@ static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl) return; spin_lock(&lru_lock); - if (!list_empty(&gl->gl_lru)) { + if (test_bit(GLF_LRU, &gl->gl_flags)) { list_del_init(&gl->gl_lru); atomic_dec(&lru_count); clear_bit(GLF_LRU, &gl->gl_flags); @@ -1159,8 +1164,7 @@ void gfs2_glock_dq(struct gfs2_holder *gh) !test_bit(GLF_DEMOTE, &gl->gl_flags)) fast_path = 1; } - if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl) && - (glops->go_flags & GLOF_LRU)) + if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl)) gfs2_glock_add_to_lru(gl); trace_gfs2_glock_queue(gh, 0); @@ -1456,6 +1460,7 @@ __acquires(&lru_lock) if (!spin_trylock(&gl->gl_lockref.lock)) { add_back_to_lru: list_add(&gl->gl_lru, &lru_list); + set_bit(GLF_LRU, &gl->gl_flags); atomic_inc(&lru_count); continue; } @@ -1463,7 +1468,6 @@ add_back_to_lru: spin_unlock(&gl->gl_lockref.lock); goto add_back_to_lru; } - clear_bit(GLF_LRU, &gl->gl_flags); gl->gl_lockref.count++; if (demote_ok(gl)) handle_callback(gl, LM_ST_UNLOCKED, 0, false); @@ -1498,6 +1502,7 @@ static long gfs2_scan_glock_lru(int nr) if (!test_bit(GLF_LOCK, &gl->gl_flags)) { list_move(&gl->gl_lru, &dispose); atomic_dec(&lru_count); + clear_bit(GLF_LRU, &gl->gl_flags); freed++; continue; } @@ -1796,7 +1801,7 @@ void gfs2_dump_glock(struct seq_file *seq, struct gfs2_glock *gl) state2str(gl->gl_target), state2str(gl->gl_demote_state), dtime, atomic_read(&gl->gl_ail_count), - atomic_read(&gl->gl_revokes), + test_bit(GLF_REVOKES, &gl->gl_flags) ? 1 : 0, (int)gl->gl_lockref.count, gl->gl_hold_time); list_for_each_entry(gh, &gl->gl_holders, gh_list) diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 78510ab91835..24ada3ccc525 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -28,6 +28,7 @@ #include "util.h" #include "trans.h" #include "dir.h" +#include "lops.h" struct workqueue_struct *gfs2_freeze_wq; @@ -531,7 +532,7 @@ static int freeze_go_xmote_bh(struct gfs2_glock *gl, struct gfs2_holder *gh) if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) { j_gl->gl_ops->go_inval(j_gl, DIO_METADATA); - error = gfs2_find_jhead(sdp->sd_jdesc, &head); + error = gfs2_find_jhead(sdp->sd_jdesc, &head, false); if (error) gfs2_consist(sdp); if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index cdf07b408f54..b15755068593 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -345,6 +345,7 @@ enum { GLF_OBJECT = 14, /* Used only for tracing */ GLF_BLOCKING = 15, GLF_INODE_CREATING = 16, /* Inode creation occurring */ + GLF_REVOKES = 17, /* Glock has revokes in queue */ }; struct gfs2_glock { @@ -374,7 +375,6 @@ struct gfs2_glock { struct list_head gl_lru; struct list_head gl_ail_list; atomic_t gl_ail_count; - atomic_t gl_revokes; struct delayed_work gl_work; union { /* For inode and iopen glocks only */ @@ -535,7 +535,7 @@ struct gfs2_jdesc { unsigned long jd_flags; #define JDF_RECOVERY 1 unsigned int jd_jid; - unsigned int jd_blocks; + u32 jd_blocks; int jd_recover_error; /* Replay stuff */ @@ -621,6 +621,7 @@ enum { SDF_SKIP_DLM_UNLOCK = 8, SDF_FORCE_AIL_FLUSH = 9, SDF_AIL1_IO_ERROR = 10, + SDF_FS_FROZEN = 11, }; enum gfs2_freeze_state { @@ -809,8 +810,8 @@ struct gfs2_sbd { atomic_t sd_log_pinned; unsigned int sd_log_num_revoke; - struct list_head sd_log_le_revoke; - struct list_head sd_log_le_ordered; + struct list_head sd_log_revokes; + struct list_head sd_log_ordered; spinlock_t sd_ordered_lock; atomic_t sd_log_thresh1; diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index b8830fda51e8..a2e1df488df0 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -551,9 +551,9 @@ static void gfs2_ordered_write(struct gfs2_sbd *sdp) LIST_HEAD(written); spin_lock(&sdp->sd_ordered_lock); - list_sort(NULL, &sdp->sd_log_le_ordered, &ip_cmp); - while (!list_empty(&sdp->sd_log_le_ordered)) { - ip = list_entry(sdp->sd_log_le_ordered.next, struct gfs2_inode, i_ordered); + list_sort(NULL, &sdp->sd_log_ordered, &ip_cmp); + while (!list_empty(&sdp->sd_log_ordered)) { + ip = list_entry(sdp->sd_log_ordered.next, struct gfs2_inode, i_ordered); if (ip->i_inode.i_mapping->nrpages == 0) { test_and_clear_bit(GIF_ORDERED, &ip->i_flags); list_del(&ip->i_ordered); @@ -564,7 +564,7 @@ static void gfs2_ordered_write(struct gfs2_sbd *sdp) filemap_fdatawrite(ip->i_inode.i_mapping); spin_lock(&sdp->sd_ordered_lock); } - list_splice(&written, &sdp->sd_log_le_ordered); + list_splice(&written, &sdp->sd_log_ordered); spin_unlock(&sdp->sd_ordered_lock); } @@ -573,8 +573,8 @@ static void gfs2_ordered_wait(struct gfs2_sbd *sdp) struct gfs2_inode *ip; spin_lock(&sdp->sd_ordered_lock); - while (!list_empty(&sdp->sd_log_le_ordered)) { - ip = list_entry(sdp->sd_log_le_ordered.next, struct gfs2_inode, i_ordered); + while (!list_empty(&sdp->sd_log_ordered)) { + ip = list_entry(sdp->sd_log_ordered.next, struct gfs2_inode, i_ordered); list_del(&ip->i_ordered); WARN_ON(!test_and_clear_bit(GIF_ORDERED, &ip->i_flags)); if (ip->i_inode.i_mapping->nrpages == 0) @@ -606,9 +606,12 @@ void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd) gfs2_remove_from_ail(bd); /* drops ref on bh */ bd->bd_bh = NULL; sdp->sd_log_num_revoke++; - atomic_inc(&gl->gl_revokes); + if (!test_bit(GLF_REVOKES, &gl->gl_flags)) { + set_bit(GLF_REVOKES, &gl->gl_flags); + gfs2_glock_hold(gl); + } set_bit(GLF_LFLUSH, &gl->gl_flags); - list_add(&bd->bd_list, &sdp->sd_log_le_revoke); + list_add(&bd->bd_list, &sdp->sd_log_revokes); } void gfs2_write_revokes(struct gfs2_sbd *sdp) @@ -666,11 +669,12 @@ out_of_blocks: } /** - * write_log_header - Write a journal log header buffer at sd_log_flush_head + * 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 * @@ -678,7 +682,8 @@ out_of_blocks: */ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, - u64 seq, u32 tail, u32 flags, int op_flags) + u64 seq, u32 tail, u32 lblock, u32 flags, + int op_flags) { struct gfs2_log_header *lh; u32 hash, crc; @@ -686,7 +691,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local; struct timespec64 tv; struct super_block *sb = sdp->sd_vfs; - u64 addr; + u64 dblock; lh = page_address(page); clear_page(lh); @@ -699,15 +704,21 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, lh->lh_sequence = cpu_to_be64(seq); lh->lh_flags = cpu_to_be32(flags); lh->lh_tail = cpu_to_be32(tail); - lh->lh_blkno = cpu_to_be32(sdp->sd_log_flush_head); + lh->lh_blkno = cpu_to_be32(lblock); hash = ~crc32(~0, lh, LH_V1_SIZE); lh->lh_hash = cpu_to_be32(hash); ktime_get_coarse_real_ts64(&tv); lh->lh_nsec = cpu_to_be32(tv.tv_nsec); lh->lh_sec = cpu_to_be64(tv.tv_sec); - addr = gfs2_log_bmap(sdp); - lh->lh_addr = cpu_to_be64(addr); + if (!list_empty(&jd->extent_list)) + dblock = gfs2_log_bmap(sdp); + else { + int ret = gfs2_lblk_to_dblk(jd->jd_inode, lblock, &dblock); + 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 @@ -732,8 +743,8 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, sb->s_blocksize - LH_V1_SIZE - 4); lh->lh_crc = cpu_to_be32(crc); - gfs2_log_write(sdp, page, sb->s_blocksize, 0, addr); - gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE, op_flags); + gfs2_log_write(sdp, page, sb->s_blocksize, 0, dblock); + gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE | op_flags); log_flush_wait(sdp); } @@ -761,7 +772,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags) } sdp->sd_log_idle = (tail == sdp->sd_log_flush_head); gfs2_write_log_header(sdp, sdp->sd_jdesc, sdp->sd_log_sequence++, tail, - flags, op_flags); + sdp->sd_log_flush_head, flags, op_flags); if (sdp->sd_log_tail != tail) log_pull_tail(sdp, tail); @@ -810,7 +821,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) gfs2_ordered_write(sdp); lops_before_commit(sdp, tr); - gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE, 0); + gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE); if (sdp->sd_log_head != sdp->sd_log_flush_head) { log_flush_wait(sdp); diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h index 1bc9bd444b28..7a34a3234266 100644 --- a/fs/gfs2/log.h +++ b/fs/gfs2/log.h @@ -59,7 +59,7 @@ static inline void gfs2_ordered_add_inode(struct gfs2_inode *ip) if (!test_bit(GIF_ORDERED, &ip->i_flags)) { spin_lock(&sdp->sd_ordered_lock); if (!test_and_set_bit(GIF_ORDERED, &ip->i_flags)) - list_add(&ip->i_ordered, &sdp->sd_log_le_ordered); + list_add(&ip->i_ordered, &sdp->sd_log_ordered); spin_unlock(&sdp->sd_ordered_lock); } } @@ -70,7 +70,8 @@ extern unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct, extern void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks); extern int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks); extern void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, - u64 seq, u32 tail, u32 flags, int op_flags); + u64 seq, u32 tail, u32 lblock, u32 flags, + int op_flags); extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 type); extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans); diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index 6f09b5e3dd6e..33ab662c9aac 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -17,7 +17,9 @@ #include <linux/bio.h> #include <linux/fs.h> #include <linux/list_sort.h> +#include <linux/blkdev.h> +#include "bmap.h" #include "dir.h" #include "gfs2.h" #include "incore.h" @@ -194,7 +196,6 @@ static void gfs2_end_log_write_bh(struct gfs2_sbd *sdp, /** * gfs2_end_log_write - end of i/o to the log * @bio: The bio - * @error: Status of i/o request * * Each bio_vec contains either data from the pagecache or data * relating to the log itself. Here we iterate over the bio_vec @@ -231,20 +232,19 @@ static void gfs2_end_log_write(struct bio *bio) /** * gfs2_log_submit_bio - Submit any pending log bio * @biop: Address of the bio pointer - * @op: REQ_OP - * @op_flags: req_flag_bits + * @opf: REQ_OP | op_flags * * Submit any pending part-built or full bio to the block device. If * there is no pending bio, then this is a no-op. */ -void gfs2_log_submit_bio(struct bio **biop, int op, int op_flags) +void gfs2_log_submit_bio(struct bio **biop, int opf) { struct bio *bio = *biop; if (bio) { struct gfs2_sbd *sdp = bio->bi_private; atomic_inc(&sdp->sd_log_in_flight); - bio_set_op_attrs(bio, op, op_flags); + bio->bi_opf = opf; submit_bio(bio); *biop = NULL; } @@ -305,7 +305,7 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno, nblk >>= sdp->sd_fsb2bb_shift; if (blkno == nblk && !flush) return bio; - gfs2_log_submit_bio(biop, op, 0); + gfs2_log_submit_bio(biop, op); } *biop = gfs2_log_alloc_bio(sdp, blkno, end_io); @@ -376,6 +376,205 @@ void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page) gfs2_log_bmap(sdp)); } +/** + * gfs2_end_log_read - end I/O callback for reads from the log + * @bio: The bio + * + * Simply unlock the pages in the bio. The main thread will wait on them and + * process them in order as necessary. + */ + +static void gfs2_end_log_read(struct bio *bio) +{ + struct page *page; + struct bio_vec *bvec; + struct bvec_iter_all iter_all; + + bio_for_each_segment_all(bvec, bio, iter_all) { + page = bvec->bv_page; + if (bio->bi_status) { + int err = blk_status_to_errno(bio->bi_status); + + SetPageError(page); + mapping_set_error(page->mapping, err); + } + unlock_page(page); + } + + bio_put(bio); +} + +/** + * gfs2_jhead_pg_srch - Look for the journal head in a given page. + * @jd: The journal descriptor + * @page: The page to look in + * + * Returns: 1 if found, 0 otherwise. + */ + +static bool gfs2_jhead_pg_srch(struct gfs2_jdesc *jd, + struct gfs2_log_header_host *head, + struct page *page) +{ + struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); + struct gfs2_log_header_host uninitialized_var(lh); + void *kaddr = kmap_atomic(page); + unsigned int offset; + bool ret = false; + + for (offset = 0; offset < PAGE_SIZE; offset += sdp->sd_sb.sb_bsize) { + if (!__get_log_header(sdp, kaddr + offset, 0, &lh)) { + if (lh.lh_sequence > head->lh_sequence) + *head = lh; + else { + ret = true; + break; + } + } + } + kunmap_atomic(kaddr); + return ret; +} + +/** + * gfs2_jhead_process_page - Search/cleanup a page + * @jd: The journal descriptor + * @index: Index of the page to look into + * @done: If set, perform only cleanup, else search and set if found. + * + * Find the page with 'index' in the journal's mapping. Search the page for + * the journal head if requested (cleanup == false). Release refs on the + * page so the page cache can reclaim it (put_page() twice). We grabbed a + * reference on this page two times, first when we did a find_or_create_page() + * to obtain the page to add it to the bio and second when we do a + * find_get_page() here to get the page to wait on while I/O on it is being + * completed. + * This function is also used to free up a page we might've grabbed but not + * used. Maybe we added it to a bio, but not submitted it for I/O. Or we + * submitted the I/O, but we already found the jhead so we only need to drop + * our references to the page. + */ + +static void gfs2_jhead_process_page(struct gfs2_jdesc *jd, unsigned long index, + struct gfs2_log_header_host *head, + bool *done) +{ + struct page *page; + + page = find_get_page(jd->jd_inode->i_mapping, index); + wait_on_page_locked(page); + + if (PageError(page)) + *done = true; + + if (!*done) + *done = gfs2_jhead_pg_srch(jd, head, page); + + put_page(page); /* Once for find_get_page */ + put_page(page); /* Once more for find_or_create_page */ +} + +/** + * gfs2_find_jhead - find the head of a log + * @jd: The journal descriptor + * @head: The log descriptor for the head of the log is returned here + * + * Do a search of a journal by reading it in large chunks using bios and find + * the valid log entry with the highest sequence number. (i.e. the log head) + * + * Returns: 0 on success, errno otherwise + */ +int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head, + bool keep_cache) +{ + struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); + struct address_space *mapping = jd->jd_inode->i_mapping; + unsigned int block = 0, blocks_submitted = 0, blocks_read = 0; + unsigned int bsize = sdp->sd_sb.sb_bsize; + unsigned int bsize_shift = sdp->sd_sb.sb_bsize_shift; + unsigned int shift = PAGE_SHIFT - bsize_shift; + unsigned int readhead_blocks = BIO_MAX_PAGES << shift; + struct gfs2_journal_extent *je; + int sz, ret = 0; + struct bio *bio = NULL; + struct page *page = NULL; + bool done = false; + errseq_t since; + + memset(head, 0, sizeof(*head)); + if (list_empty(&jd->extent_list)) + gfs2_map_journal_extents(sdp, jd); + + since = filemap_sample_wb_err(mapping); + list_for_each_entry(je, &jd->extent_list, list) { + for (; block < je->lblock + je->blocks; block++) { + u64 dblock; + + if (!page) { + page = find_or_create_page(mapping, + block >> shift, GFP_NOFS); + if (!page) { + ret = -ENOMEM; + done = true; + goto out; + } + } + + if (bio) { + unsigned int off; + + off = (block << bsize_shift) & ~PAGE_MASK; + sz = bio_add_page(bio, page, bsize, off); + if (sz == bsize) { /* block added */ + if (off + bsize == PAGE_SIZE) { + page = NULL; + goto page_added; + } + continue; + } + blocks_submitted = block + 1; + submit_bio(bio); + bio = NULL; + } + + dblock = je->dblock + (block - je->lblock); + bio = gfs2_log_alloc_bio(sdp, dblock, gfs2_end_log_read); + bio->bi_opf = REQ_OP_READ; + sz = bio_add_page(bio, page, bsize, 0); + gfs2_assert_warn(sdp, sz == bsize); + if (bsize == PAGE_SIZE) + page = NULL; + +page_added: + if (blocks_submitted < blocks_read + readhead_blocks) { + /* Keep at least one bio in flight */ + continue; + } + + gfs2_jhead_process_page(jd, blocks_read >> shift, head, &done); + blocks_read += PAGE_SIZE >> bsize_shift; + if (done) + goto out; /* found */ + } + } + +out: + if (bio) + submit_bio(bio); + while (blocks_read < block) { + gfs2_jhead_process_page(jd, blocks_read >> shift, head, &done); + blocks_read += PAGE_SIZE >> bsize_shift; + } + + if (!ret) + ret = filemap_check_wb_err(mapping, since); + + if (!keep_cache) + truncate_inode_pages(mapping, 0); + + return ret; +} + static struct page *gfs2_get_log_desc(struct gfs2_sbd *sdp, u32 ld_type, u32 ld_length, u32 ld_data1) { @@ -529,7 +728,7 @@ static void buf_lo_before_scan(struct gfs2_jdesc *jd, jd->jd_replayed_blocks = 0; } -static int buf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start, +static int buf_lo_scan_elements(struct gfs2_jdesc *jd, u32 start, struct gfs2_log_descriptor *ld, __be64 *ptr, int pass) { @@ -622,7 +821,7 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) { struct gfs2_meta_header *mh; unsigned int offset; - struct list_head *head = &sdp->sd_log_le_revo |