Merge tag 'gfs2-for-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2

Pull gfs2 updates from Andreas Gruenbacher:

 - Fix possible data loss during inode evict

 - Fix a race during bufdata allocation

 - More careful cleaning up during a withdraw

 - Prevent excessive log flushing under memory pressure

 - Various other minor fixes and cleanups

* tag 'gfs2-for-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
  gfs2: prevent NULL pointer dereference during unmount
  gfs2: hide error messages after withdraw
  gfs2: wait for withdraw earlier during unmount
  gfs2: inode directory consistency checks
  gfs2: gfs2_log_flush withdraw fixes
  gfs2: add some missing log locking
  gfs2: fix address space truncation during withdraw
  gfs2: drain ail under sd_log_flush_lock
  gfs2: bufdata allocation race
  gfs2: Remove trans_drain code duplication
  gfs2: Move gfs2_remove_from_journal to log.c
  gfs2: Get rid of gfs2_log_[un]lock helpers
  gfs2: less aggressive low-memory log flushing
  gfs2: Fix data loss during inode evict
  gfs2: minor evict_[un]linked_inode cleanup
  gfs2: Avoid unnecessary transactions in evict_linked_inode
  gfs2: Remove unnecessary check in gfs2_evict_inode
  gfs2: Call unlock_new_inode before d_instantiate
This commit is contained in:
Linus Torvalds
2026-04-15 19:12:04 -07:00
13 changed files with 237 additions and 170 deletions

View File

@@ -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;
}

View File

@@ -1539,7 +1539,7 @@ more_rgrps:
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)

View File

@@ -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;

View File

@@ -892,7 +892,7 @@ retry:
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 @@ retry:
gfs2_glock_dq_uninit(&gh);
gfs2_glock_put(io_gl);
gfs2_qa_put(dip);
unlock_new_inode(inode);
return error;
fail_gunlock4:

View File

@@ -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 @@ repeat:
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 @@ repeat:
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 @@ out:
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 @@ out_withdraw:
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;

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -596,6 +596,9 @@ restart:
}
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 @@ restart:
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);

View File

@@ -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)

View File

@@ -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);