mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
127
fs/gfs2/log.c
127
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 @@ 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
111
fs/gfs2/super.c
111
fs/gfs2/super.c
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user