diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index dae3dc4ee6f7..0a7b8076af3a 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -158,6 +158,7 @@ static int gfs2_writepages(struct address_space *mapping, struct writeback_control *wbc) { struct gfs2_sbd *sdp = gfs2_mapping2sbd(mapping); + long initial_nr_to_write = wbc->nr_to_write; struct iomap_writepage_ctx wpc = { .inode = mapping->host, .wbc = wbc, @@ -166,13 +167,13 @@ static int gfs2_writepages(struct address_space *mapping, int ret; /* - * Even if we didn't write enough pages here, we might still be holding + * Even if we didn't write any pages here, we might still be holding * dirty pages in the ail. We forcibly flush the ail because we don't * want balance_dirty_pages() to loop indefinitely trying to write out * pages held in the ail that it can't find. */ ret = iomap_writepages(&wpc); - if (ret == 0 && wbc->nr_to_write > 0) + if (ret == 0 && wbc->nr_to_write == initial_nr_to_write) set_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags); return ret; } @@ -582,7 +583,7 @@ static void gfs2_discard(struct gfs2_sbd *sdp, struct buffer_head *bh) struct gfs2_bufdata *bd; lock_buffer(bh); - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); clear_buffer_dirty(bh); bd = bh->b_private; if (bd) { @@ -598,7 +599,7 @@ static void gfs2_discard(struct gfs2_sbd *sdp, struct buffer_head *bh) clear_buffer_mapped(bh); clear_buffer_req(bh); clear_buffer_new(bh); - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); unlock_buffer(bh); } @@ -666,7 +667,7 @@ bool gfs2_release_folio(struct folio *folio, gfp_t gfp_mask) * again. */ - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); bh = head; do { if (atomic_read(&bh->b_count)) @@ -698,12 +699,12 @@ bool gfs2_release_folio(struct folio *folio, gfp_t gfp_mask) bh = bh->b_this_page; } while (bh != head); - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); return try_to_free_buffers(folio); cannot_release: - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); return false; } diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 1cd8ec0bce83..b3d7fcd95f03 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1539,7 +1539,7 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh, revokes = jblocks_rqsted; if (meta) revokes += end - start; - else if (ip->i_depth) + else if (ip->i_diskflags & GFS2_DIF_EXHASH) revokes += sdp->sd_inptrs; ret = gfs2_trans_begin(sdp, jblocks_rqsted, revokes); if (ret) diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index ba61649368bf..28f32424ee64 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -64,7 +64,7 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync, struct buffer_head *bh; const unsigned long b_state = (1UL << BH_Dirty)|(1UL << BH_Pinned)|(1UL << BH_Lock); - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); spin_lock(&sdp->sd_ail_lock); list_for_each_entry_safe_reverse(bd, tmp, head, bd_ail_gl_list) { if (nr_revokes == 0) @@ -80,7 +80,7 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync, } GLOCK_BUG_ON(gl, !fsync && atomic_read(&gl->gl_ail_count)); spin_unlock(&sdp->sd_ail_lock); - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); } @@ -109,10 +109,10 @@ static int gfs2_ail_empty_gl(struct gfs2_glock *gl) * If none of these conditions are true, our revokes are all * flushed and we can return. */ - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); have_revokes = !list_empty(&sdp->sd_log_revokes); log_in_flight = atomic_read(&sdp->sd_log_in_flight); - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); if (have_revokes) goto flush; if (log_in_flight) @@ -457,6 +457,11 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) ip->i_depth = (u8)depth; ip->i_entries = be32_to_cpu(str->di_entries); + if (!S_ISDIR(inode->i_mode) && (ip->i_diskflags & GFS2_DIF_EXHASH)) { + gfs2_consist_inode(ip); + return -EIO; + } + if (gfs2_is_stuffed(ip) && inode->i_size > gfs2_max_stuffed_size(ip)) { gfs2_consist_inode(ip); return -EIO; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 8344040ecaf7..e9bf4879c07f 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -892,7 +892,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, goto fail_gunlock4; mark_inode_dirty(inode); - d_instantiate(dentry, inode); + d_instantiate_new(dentry, inode); /* After instantiate, errors should result in evict which will destroy * both inode and iopen glocks properly. */ if (file) { @@ -904,7 +904,6 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, gfs2_glock_dq_uninit(&gh); gfs2_glock_put(io_gl); gfs2_qa_put(dip); - unlock_new_inode(inode); return error; fail_gunlock4: diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 347df29d610e..78bba8cc10b8 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -72,7 +72,7 @@ unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct) * */ -void gfs2_remove_from_ail(struct gfs2_bufdata *bd) +static void gfs2_remove_from_ail(struct gfs2_bufdata *bd) { bd->bd_tr = NULL; list_del_init(&bd->bd_ail_st_list); @@ -467,8 +467,9 @@ void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks) { atomic_add(blks, &sdp->sd_log_blks_free); trace_gfs2_log_blocks(sdp, blks); - gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <= - sdp->sd_jdesc->jd_blocks); + gfs2_assert_withdraw(sdp, !sdp->sd_jdesc || + atomic_read(&sdp->sd_log_blks_free) <= + sdp->sd_jdesc->jd_blocks); if (atomic_read(&sdp->sd_log_blks_needed)) wake_up(&sdp->sd_log_waitq); } @@ -800,9 +801,9 @@ void gfs2_flush_revokes(struct gfs2_sbd *sdp) /* number of revokes we still have room for */ unsigned int max_revokes = atomic_read(&sdp->sd_log_revokes_available); - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); gfs2_ail1_empty(sdp, max_revokes); - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); } /** @@ -983,49 +984,90 @@ static void empty_ail1_list(struct gfs2_sbd *sdp) } } +static void gfs2_trans_drain_list(struct gfs2_sbd *sdp, struct list_head *list) +{ + struct gfs2_bufdata *bd; + + while (!list_empty(list)) { + bd = list_first_entry(list, struct gfs2_bufdata, bd_list); + struct buffer_head *bh = bd->bd_bh; + + WARN_ON_ONCE(!buffer_pinned(bh)); + clear_buffer_pinned(bh); + trace_gfs2_pin(bd, 0); + atomic_dec(&sdp->sd_log_pinned); + list_del_init(&bd->bd_list); + brelse(bh); + } +} + /** - * trans_drain - drain the buf and databuf queue for a failed transaction + * gfs2_trans_drain - drain the buf and databuf queue for a failed transaction + * @sdp: the filesystem * @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. */ -static void trans_drain(struct gfs2_trans *tr) +static void gfs2_trans_drain(struct gfs2_sbd *sdp, struct gfs2_trans *tr) { - struct gfs2_bufdata *bd; - struct list_head *head; - if (!tr) return; + gfs2_trans_drain_list(sdp, &tr->tr_buf); + gfs2_trans_drain_list(sdp, &tr->tr_databuf); +} - head = &tr->tr_buf; - while (!list_empty(head)) { - bd = list_first_entry(head, struct gfs2_bufdata, bd_list); +void gfs2_remove_from_journal(struct buffer_head *bh, int meta) +{ + struct address_space *mapping = bh->b_folio->mapping; + struct gfs2_sbd *sdp = gfs2_mapping2sbd(mapping); + struct gfs2_bufdata *bd = bh->b_private; + struct gfs2_trans *tr = current->journal_info; + int was_pinned = 0; + + if (test_clear_buffer_pinned(bh)) { + trace_gfs2_pin(bd, 0); + atomic_dec(&sdp->sd_log_pinned); 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); + if (tr) { + if (meta == REMOVE_META) + tr->tr_num_buf_rm++; + else + tr->tr_num_databuf_rm++; + set_bit(TR_TOUCHED, &tr->tr_flags); + } + was_pinned = 1; + brelse(bh); } - 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)) + if (bd) { + if (bd->bd_tr) { + if (tr) + gfs2_trans_add_revoke(sdp, bd); + else + gfs2_remove_from_ail(bd); + } else if (was_pinned) { + bh->b_private = NULL; + kmem_cache_free(gfs2_bufdata_cachep, bd); + } else if (!list_empty(&bd->bd_ail_st_list) && + !list_empty(&bd->bd_ail_gl_list)) { gfs2_remove_from_ail(bd); - kmem_cache_free(gfs2_bufdata_cachep, bd); + } } + clear_buffer_dirty(bh); + clear_buffer_uptodate(bh); } /** - * gfs2_log_flush - flush incore transaction(s) + * __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 * */ -void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) +static void __gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, + u32 flags) { struct gfs2_trans *tr = NULL; unsigned int reserved_blocks = 0, used_blocks = 0; @@ -1033,7 +1075,6 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) unsigned int first_log_head; unsigned int reserved_revokes = 0; - down_write(&sdp->sd_log_flush_lock); trace_gfs2_log_flush(sdp, 1, flags); repeat: @@ -1110,7 +1151,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) goto out_withdraw; lops_after_commit(sdp, tr); - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); sdp->sd_log_blks_reserved = 0; spin_lock(&sdp->sd_ail_lock); @@ -1119,7 +1160,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) tr = NULL; } spin_unlock(&sdp->sd_ail_lock); - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); if (!(flags & GFS2_LOG_HEAD_FLUSH_NORMAL)) { if (!sdp->sd_log_idle) { @@ -1145,13 +1186,16 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) gfs2_assert_withdraw(sdp, used_blocks < reserved_blocks); gfs2_log_release(sdp, reserved_blocks - used_blocks); } - up_write(&sdp->sd_log_flush_lock); gfs2_trans_free(sdp, tr); trace_gfs2_log_flush(sdp, 0, flags); return; out_withdraw: - trans_drain(tr); + if (sdp->sd_jdesc->jd_log_bio) { + bio_io_error(sdp->sd_jdesc->jd_log_bio); + sdp->sd_jdesc->jd_log_bio = NULL; + } + gfs2_trans_drain(sdp, tr); /** * If the tr_list is empty, we're withdrawing during a log * flush that targets a transaction, but the transaction was @@ -1166,6 +1210,13 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) goto out_end; } +void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) +{ + down_write(&sdp->sd_log_flush_lock); + __gfs2_log_flush(sdp, gl, flags); + up_write(&sdp->sd_log_flush_lock); +} + /** * gfs2_merge_trans - Merge a new transaction into a cached transaction * @sdp: the filesystem @@ -1200,7 +1251,7 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) unsigned int unused; unsigned int maxres; - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); if (sdp->sd_log_tr) { gfs2_merge_trans(sdp, tr); @@ -1218,7 +1269,7 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) gfs2_log_release(sdp, unused); sdp->sd_log_blks_reserved = reserved; - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); } static inline int gfs2_jrnl_flush_reqd(struct gfs2_sbd *sdp) @@ -1297,19 +1348,25 @@ int gfs2_logd(void *data) break; if (gfs2_jrnl_flush_reqd(sdp) || t == 0) { + down_write(&sdp->sd_log_flush_lock); gfs2_ail1_empty(sdp, 0); - gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL | - GFS2_LFC_LOGD_JFLUSH_REQD); + __gfs2_log_flush(sdp, NULL, + GFS2_LOG_HEAD_FLUSH_NORMAL | + GFS2_LFC_LOGD_JFLUSH_REQD); + up_write(&sdp->sd_log_flush_lock); } if (test_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags) || gfs2_ail_flush_reqd(sdp)) { clear_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags); + down_write(&sdp->sd_log_flush_lock); gfs2_ail1_start(sdp); gfs2_ail1_wait(sdp); gfs2_ail1_empty(sdp, 0); - gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL | - GFS2_LFC_LOGD_AIL_FLUSH_REQD); + __gfs2_log_flush(sdp, NULL, + GFS2_LOG_HEAD_FLUSH_NORMAL | + GFS2_LFC_LOGD_AIL_FLUSH_REQD); + up_write(&sdp->sd_log_flush_lock); } t = gfs2_tune_get(sdp, gt_logd_secs) * HZ; diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h index fc30ebdad83a..10beda62427c 100644 --- a/fs/gfs2/log.h +++ b/fs/gfs2/log.h @@ -20,30 +20,6 @@ */ #define GFS2_LOG_FLUSH_MIN_BLOCKS 4 -/** - * gfs2_log_lock - acquire the right to mess with the log manager - * @sdp: the filesystem - * - */ - -static inline void gfs2_log_lock(struct gfs2_sbd *sdp) -__acquires(&sdp->sd_log_lock) -{ - spin_lock(&sdp->sd_log_lock); -} - -/** - * gfs2_log_unlock - release the right to mess with the log manager - * @sdp: the filesystem - * - */ - -static inline void gfs2_log_unlock(struct gfs2_sbd *sdp) -__releases(&sdp->sd_log_lock) -{ - spin_unlock(&sdp->sd_log_lock); -} - static inline void gfs2_ordered_add_inode(struct gfs2_inode *ip) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); @@ -61,7 +37,6 @@ static inline void gfs2_ordered_add_inode(struct gfs2_inode *ip) void gfs2_ordered_del_inode(struct gfs2_inode *ip); unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct); -void gfs2_remove_from_ail(struct gfs2_bufdata *bd); bool gfs2_log_is_empty(struct gfs2_sbd *sdp); void gfs2_log_release_revokes(struct gfs2_sbd *sdp, unsigned int revokes); void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks); @@ -72,6 +47,7 @@ void gfs2_log_reserve(struct gfs2_sbd *sdp, struct gfs2_trans *tr, void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, u64 seq, u32 tail, u32 lblock, u32 flags, blk_opf_t op_flags); +void gfs2_remove_from_journal(struct buffer_head *bh, int meta); void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 type); 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 e03928def87e..6dabe73ad790 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -648,19 +648,19 @@ static void gfs2_before_commit(struct gfs2_sbd *sdp, unsigned int limit, unsigned n; __be64 *ptr; - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); list_sort(NULL, blist, blocknr_cmp); bd1 = bd2 = list_prepare_entry(bd1, blist, bd_list); while(total) { num = total; if (total > limit) num = limit; - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); page = gfs2_get_log_desc(sdp, is_databuf ? GFS2_LOG_DESC_JDATA : GFS2_LOG_DESC_METADATA, num + 1, num); ld = page_address(page); - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); ptr = (__be64 *)(ld + 1); n = 0; @@ -674,14 +674,14 @@ static void gfs2_before_commit(struct gfs2_sbd *sdp, unsigned int limit, break; } - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); gfs2_log_write_page(sdp, page); - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); n = 0; list_for_each_entry_continue(bd2, blist, bd_list) { get_bh(bd2->bd_bh); - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); lock_buffer(bd2->bd_bh); if (buffer_escaped(bd2->bd_bh)) { @@ -698,7 +698,7 @@ static void gfs2_before_commit(struct gfs2_sbd *sdp, unsigned int limit, } else { gfs2_log_write_bh(sdp, bd2->bd_bh); } - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); if (++n >= num) break; } @@ -706,7 +706,7 @@ static void gfs2_before_commit(struct gfs2_sbd *sdp, unsigned int limit, BUG_ON(total < num); total -= num; } - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); } static void buf_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index 3c8e4553102d..d407dd476e72 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -338,41 +338,6 @@ int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh) return 0; } -void gfs2_remove_from_journal(struct buffer_head *bh, int meta) -{ - struct address_space *mapping = bh->b_folio->mapping; - struct gfs2_sbd *sdp = gfs2_mapping2sbd(mapping); - struct gfs2_bufdata *bd = bh->b_private; - struct gfs2_trans *tr = current->journal_info; - int was_pinned = 0; - - if (test_clear_buffer_pinned(bh)) { - trace_gfs2_pin(bd, 0); - atomic_dec(&sdp->sd_log_pinned); - list_del_init(&bd->bd_list); - if (meta == REMOVE_META) - tr->tr_num_buf_rm++; - else - tr->tr_num_databuf_rm++; - set_bit(TR_TOUCHED, &tr->tr_flags); - was_pinned = 1; - brelse(bh); - } - if (bd) { - if (bd->bd_tr) { - gfs2_trans_add_revoke(sdp, bd); - } else if (was_pinned) { - bh->b_private = NULL; - kmem_cache_free(gfs2_bufdata_cachep, bd); - } else if (!list_empty(&bd->bd_ail_st_list) && - !list_empty(&bd->bd_ail_gl_list)) { - gfs2_remove_from_ail(bd); - } - } - clear_buffer_dirty(bh); - clear_buffer_uptodate(bh); -} - /** * gfs2_ail1_wipe - remove deleted/freed buffers from the ail1 list * @sdp: superblock @@ -391,7 +356,7 @@ static void gfs2_ail1_wipe(struct gfs2_sbd *sdp, u64 bstart, u32 blen) struct buffer_head *bh; u64 end = bstart + blen; - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); spin_lock(&sdp->sd_ail_lock); list_for_each_entry_safe(tr, s, &sdp->sd_ail1_list, tr_list) { list_for_each_entry_safe(bd, bs, &tr->tr_ail1_list, @@ -404,7 +369,7 @@ static void gfs2_ail1_wipe(struct gfs2_sbd *sdp, u64 bstart, u32 blen) } } spin_unlock(&sdp->sd_ail_lock); - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); } static struct buffer_head *gfs2_getjdatabuf(struct gfs2_inode *ip, u64 blkno) @@ -456,11 +421,11 @@ void gfs2_journal_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen) } if (bh) { lock_buffer(bh); - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); spin_lock(&sdp->sd_ail_lock); gfs2_remove_from_journal(bh, ty); spin_unlock(&sdp->sd_ail_lock); - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); unlock_buffer(bh); brelse(bh); } diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h index 2fe5dec193ed..ec76215bfb1a 100644 --- a/fs/gfs2/meta_io.h +++ b/fs/gfs2/meta_io.h @@ -59,7 +59,6 @@ enum { REMOVE_META = 1, }; -void gfs2_remove_from_journal(struct buffer_head *bh, int meta); void gfs2_journal_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen); int gfs2_meta_buffer(struct gfs2_inode *ip, u32 mtype, u64 num, struct buffer_head **bhp); diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 441774cc07ce..5988a165a830 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -2529,7 +2529,7 @@ void __gfs2_free_blocks(struct gfs2_inode *ip, struct gfs2_rgrpd *rgd, rgrp_unlock_local(rgd); /* Directories keep their data in the metadata address space */ - if (meta || ip->i_depth || gfs2_is_jdata(ip)) + if (meta || (ip->i_diskflags & GFS2_DIF_EXHASH) || gfs2_is_jdata(ip)) gfs2_journal_wipe(ip, bstart, blen); } diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index f5fab699dee2..a2ea121331f1 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -596,6 +596,9 @@ static void gfs2_put_super(struct super_block *sb) } spin_unlock(&sdp->sd_jindex_spin); + /* Wait for withdraw to complete */ + flush_work(&sdp->sd_withdraw_work); + if (!sb_rdonly(sb)) gfs2_make_fs_ro(sdp); else { @@ -605,8 +608,6 @@ static void gfs2_put_super(struct super_block *sb) gfs2_quota_cleanup(sdp); } - flush_work(&sdp->sd_withdraw_work); - /* At this point, we're through modifying the disk */ /* Release stuff */ @@ -1241,6 +1242,9 @@ static enum evict_behavior evict_should_delete(struct inode *inode, struct gfs2_sbd *sdp = sb->s_fs_info; int ret; + if (inode->i_nlink) + return EVICT_SHOULD_SKIP_DELETE; + if (gfs2_holder_initialized(&ip->i_iopen_gh) && test_bit(GLF_DEFER_DELETE, &ip->i_iopen_gh.gh_gl->gl_flags)) return EVICT_SHOULD_DEFER_DELETE; @@ -1279,12 +1283,18 @@ static enum evict_behavior evict_should_delete(struct inode *inode, /** * evict_unlinked_inode - delete the pieces of an unlinked evicted inode * @inode: The inode to evict + * @gh: The glock holder structure */ -static int evict_unlinked_inode(struct inode *inode) +static int evict_unlinked_inode(struct inode *inode, struct gfs2_holder *gh) { struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_glock *gl = ip->i_gl; int ret; + /* The inode glock must be held exclusively and be instantiated. */ + BUG_ON(!gfs2_holder_initialized(gh) || + test_bit(GLF_INSTANTIATE_NEEDED, &gl->gl_flags)); + if (S_ISDIR(inode->i_mode) && (ip->i_diskflags & GFS2_DIF_EXHASH)) { ret = gfs2_dir_exhash_dealloc(ip); @@ -1317,44 +1327,97 @@ static int evict_unlinked_inode(struct inode *inode) */ ret = gfs2_dinode_dealloc(ip); - if (!ret && ip->i_gl) - gfs2_inode_remember_delete(ip->i_gl, ip->i_no_formal_ino); + if (!ret) + gfs2_inode_remember_delete(gl, ip->i_no_formal_ino); out: return ret; } +static int gfs2_truncate_inode_pages(struct inode *inode) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); + struct address_space *mapping = &inode->i_data; + bool need_trans = gfs2_is_jdata(ip) && mapping->nrpages; + int ret = 0; + + /* + * Truncating a jdata inode address space may create revokes in + * truncate_inode_pages() -> gfs2_invalidate_folio() -> ... -> + * gfs2_remove_from_journal(), so we need a transaction here. + * + * During a withdraw, no new transactions can be created. We still + * take the log flush lock to prevent truncate from racing with + * gfs2_log_flush(). + */ + if (need_trans) { + ret = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks); + if (ret) + down_read(&sdp->sd_log_flush_lock); + } + truncate_inode_pages(mapping, 0); + if (need_trans) { + if (ret) + up_read(&sdp->sd_log_flush_lock); + else + gfs2_trans_end(sdp); + } + return ret; +} + +static void gfs2_truncate_inode_pages_final(struct inode *inode) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); + struct address_space *mapping = &inode->i_data; + bool need_lock = gfs2_is_jdata(ip) && mapping->nrpages; + + if (need_lock) + down_read(&sdp->sd_log_flush_lock); + truncate_inode_pages_final(mapping); + if (need_lock) + up_read(&sdp->sd_log_flush_lock); +} + /* * evict_linked_inode - evict an inode whose dinode has not been unlinked * @inode: The inode to evict + * @gh: The glock holder structure */ -static int evict_linked_inode(struct inode *inode) +static int evict_linked_inode(struct inode *inode, struct gfs2_holder *gh) { struct super_block *sb = inode->i_sb; struct gfs2_sbd *sdp = sb->s_fs_info; struct gfs2_inode *ip = GFS2_I(inode); - struct address_space *metamapping; + struct gfs2_glock *gl = ip->i_gl; + struct address_space *metamapping = gfs2_glock2aspace(gl); int ret; - gfs2_log_flush(sdp, ip->i_gl, GFS2_LOG_HEAD_FLUSH_NORMAL | + if (!(test_bit(GLF_DIRTY, &gl->gl_flags) || inode->i_flags & I_DIRTY)) + goto clean; + + /* The inode glock must be held exclusively and be instantiated. */ + if (!gfs2_holder_initialized(gh)) + ret = gfs2_glock_nq_init(gl, LM_ST_EXCLUSIVE, 0, gh); + else + ret = gfs2_instantiate(gh); + if (ret) + return ret; + + gfs2_log_flush(sdp, gl, GFS2_LOG_HEAD_FLUSH_NORMAL | GFS2_LFC_EVICT_INODE); - metamapping = gfs2_glock2aspace(ip->i_gl); - if (test_bit(GLF_DIRTY, &ip->i_gl->gl_flags)) { + if (test_bit(GLF_DIRTY, &gl->gl_flags)) { filemap_fdatawrite(metamapping); filemap_fdatawait(metamapping); } write_inode_now(inode, 1); - gfs2_ail_flush(ip->i_gl, 0); + gfs2_ail_flush(gl, 0); - ret = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks); - if (ret) - return ret; - - /* Needs to be done before glock release & also in a transaction */ - truncate_inode_pages(&inode->i_data, 0); +clean: + ret = gfs2_truncate_inode_pages(inode); truncate_inode_pages(metamapping, 0); - gfs2_trans_end(sdp); - return 0; + return ret; } /** @@ -1388,7 +1451,7 @@ static void gfs2_evict_inode(struct inode *inode) int ret; gfs2_holder_mark_uninitialized(&gh); - if (inode->i_nlink || sb_rdonly(sb) || !ip->i_no_addr) + if (sb_rdonly(sb) || !ip->i_no_addr || !ip->i_gl) goto out; /* @@ -1413,19 +1476,19 @@ static void gfs2_evict_inode(struct inode *inode) behavior = EVICT_SHOULD_SKIP_DELETE; } if (behavior == EVICT_SHOULD_DELETE) - ret = evict_unlinked_inode(inode); + ret = evict_unlinked_inode(inode, &gh); else - ret = evict_linked_inode(inode); + ret = evict_linked_inode(inode, &gh); if (gfs2_rs_active(&ip->i_res)) gfs2_rs_deltree(&ip->i_res); - if (ret && ret != GLR_TRYFAILED && ret != -EROFS) + if (ret && !gfs2_withdrawn(sdp) && ret != -EROFS) fs_warn(sdp, "gfs2_evict_inode: %d\n", ret); out: if (gfs2_holder_initialized(&gh)) gfs2_glock_dq_uninit(&gh); - truncate_inode_pages_final(&inode->i_data); + gfs2_truncate_inode_pages_final(inode); if (ip->i_qadata) gfs2_assert_warn(sdp, ip->i_qadata->qa_ref == 0); gfs2_rs_deltree(&ip->i_res); diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c index 95f2632cdb01..65cbe06e301a 100644 --- a/fs/gfs2/trans.c +++ b/fs/gfs2/trans.c @@ -176,7 +176,6 @@ static struct gfs2_bufdata *gfs2_alloc_bufdata(struct gfs2_glock *gl, INIT_LIST_HEAD(&bd->bd_list); INIT_LIST_HEAD(&bd->bd_ail_st_list); INIT_LIST_HEAD(&bd->bd_ail_gl_list); - bh->b_private = bd; return bd; } @@ -205,17 +204,20 @@ void gfs2_trans_add_data(struct gfs2_glock *gl, struct buffer_head *bh) set_bit(TR_TOUCHED, &tr->tr_flags); goto out; } - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); bd = bh->b_private; if (bd == NULL) { - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); unlock_buffer(bh); - if (bh->b_private == NULL) - bd = gfs2_alloc_bufdata(gl, bh); - else - bd = bh->b_private; + bd = gfs2_alloc_bufdata(gl, bh); lock_buffer(bh); - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); + if (bh->b_private) { + kmem_cache_free(gfs2_bufdata_cachep, bd); + bd = bh->b_private; + } else { + bh->b_private = bd; + } } gfs2_assert(sdp, bd->bd_gl == gl); set_bit(TR_TOUCHED, &tr->tr_flags); @@ -226,7 +228,7 @@ void gfs2_trans_add_data(struct gfs2_glock *gl, struct buffer_head *bh) tr->tr_num_databuf_new++; list_add_tail(&bd->bd_list, &tr->tr_databuf); } - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); out: unlock_buffer(bh); } @@ -266,19 +268,20 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh) set_bit(TR_TOUCHED, &tr->tr_flags); goto out; } - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); bd = bh->b_private; if (bd == NULL) { - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); unlock_buffer(bh); - folio_lock(bh->b_folio); - if (bh->b_private == NULL) - bd = gfs2_alloc_bufdata(gl, bh); - else - bd = bh->b_private; - folio_unlock(bh->b_folio); + bd = gfs2_alloc_bufdata(gl, bh); lock_buffer(bh); - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); + if (bh->b_private) { + kmem_cache_free(gfs2_bufdata_cachep, bd); + bd = bh->b_private; + } else { + bh->b_private = bd; + } } gfs2_assert(sdp, bd->bd_gl == gl); set_bit(TR_TOUCHED, &tr->tr_flags); @@ -309,7 +312,7 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh) list_add(&bd->bd_list, &tr->tr_buf); tr->tr_num_buf_new++; out_unlock: - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); out: unlock_buffer(bh); } @@ -329,7 +332,7 @@ void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len) struct gfs2_bufdata *bd, *tmp; unsigned int n = len; - gfs2_log_lock(sdp); + spin_lock(&sdp->sd_log_lock); list_for_each_entry_safe(bd, tmp, &sdp->sd_log_revokes, bd_list) { if ((bd->bd_blkno >= blkno) && (bd->bd_blkno < (blkno + len))) { list_del_init(&bd->bd_list); @@ -343,7 +346,7 @@ void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len) break; } } - gfs2_log_unlock(sdp); + spin_unlock(&sdp->sd_log_lock); } void gfs2_trans_free(struct gfs2_sbd *sdp, struct gfs2_trans *tr) diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index 02603200846d..83b8bb6446e5 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c @@ -123,9 +123,8 @@ static void do_withdraw(struct gfs2_sbd *sdp) return; } clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); - up_write(&sdp->sd_log_flush_lock); - gfs2_ail_drain(sdp); /* frees all transactions */ + up_write(&sdp->sd_log_flush_lock); wake_up(&sdp->sd_logd_waitq); wake_up(&sdp->sd_quota_wait);