diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/gfs2/aops.c | 9 | ||||
-rw-r--r-- | fs/gfs2/aops.h | 4 | ||||
-rw-r--r-- | fs/gfs2/bmap.c | 4 | ||||
-rw-r--r-- | fs/gfs2/dentry.c | 18 | ||||
-rw-r--r-- | fs/gfs2/glock.c | 128 | ||||
-rw-r--r-- | fs/gfs2/glock.h | 4 | ||||
-rw-r--r-- | fs/gfs2/glops.c | 21 | ||||
-rw-r--r-- | fs/gfs2/incore.h | 11 | ||||
-rw-r--r-- | fs/gfs2/inode.c | 8 | ||||
-rw-r--r-- | fs/gfs2/ops_fstype.c | 71 | ||||
-rw-r--r-- | fs/gfs2/rgrp.c | 2 | ||||
-rw-r--r-- | fs/gfs2/super.c | 49 | ||||
-rw-r--r-- | fs/gfs2/sys.c | 2 |
13 files changed, 204 insertions, 127 deletions
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index e782b4f1d104..2748a82de42a 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -37,10 +37,10 @@ #include "aops.h" -void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page, - unsigned int from, unsigned int len) +void gfs2_trans_add_databufs(struct gfs2_inode *ip, struct folio *folio, + unsigned int from, unsigned int len) { - struct buffer_head *head = page_buffers(page); + struct buffer_head *head = folio_buffers(folio); unsigned int bsize = head->b_size; struct buffer_head *bh; unsigned int to = from + len; @@ -127,7 +127,6 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w { struct inode *inode = page->mapping->host; struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); if (PageChecked(page)) { ClearPageChecked(page); @@ -135,7 +134,7 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w create_empty_buffers(page, inode->i_sb->s_blocksize, BIT(BH_Dirty)|BIT(BH_Uptodate)); } - gfs2_page_add_databufs(ip, page, 0, sdp->sd_vfs->s_blocksize); + gfs2_trans_add_databufs(ip, page_folio(page), 0, PAGE_SIZE); } return gfs2_write_jdata_page(page, wbc); } diff --git a/fs/gfs2/aops.h b/fs/gfs2/aops.h index ff9877a68780..09db1914425e 100644 --- a/fs/gfs2/aops.h +++ b/fs/gfs2/aops.h @@ -9,7 +9,7 @@ #include "incore.h" extern void adjust_fs_space(struct inode *inode); -extern void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page, - unsigned int from, unsigned int len); +extern void gfs2_trans_add_databufs(struct gfs2_inode *ip, struct folio *folio, + unsigned int from, unsigned int len); #endif /* __AOPS_DOT_H__ */ diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index e191ecfb1fde..eedf6926c652 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -985,8 +985,8 @@ static void gfs2_iomap_put_folio(struct inode *inode, loff_t pos, struct gfs2_sbd *sdp = GFS2_SB(inode); if (!gfs2_is_stuffed(ip)) - gfs2_page_add_databufs(ip, &folio->page, offset_in_page(pos), - copied); + gfs2_trans_add_databufs(ip, folio, offset_in_folio(folio, pos), + copied); folio_unlock(folio); folio_put(folio); diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c index 2e215e8c3c88..6fe9ca253b70 100644 --- a/fs/gfs2/dentry.c +++ b/fs/gfs2/dentry.c @@ -83,26 +83,8 @@ static int gfs2_dhash(const struct dentry *dentry, struct qstr *str) return 0; } -static int gfs2_dentry_delete(const struct dentry *dentry) -{ - struct gfs2_inode *ginode; - - if (d_really_is_negative(dentry)) - return 0; - - ginode = GFS2_I(d_inode(dentry)); - if (!gfs2_holder_initialized(&ginode->i_iopen_gh)) - return 0; - - if (test_bit(GLF_DEMOTE, &ginode->i_iopen_gh.gh_gl->gl_flags)) - return 1; - - return 0; -} - const struct dentry_operations gfs2_dops = { .d_revalidate = gfs2_drevalidate, .d_hash = gfs2_dhash, - .d_delete = gfs2_dentry_delete, }; diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 524f3c96b9a4..5adc7d85dbf3 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -67,7 +67,6 @@ static void handle_callback(struct gfs2_glock *gl, unsigned int state, static struct dentry *gfs2_root; static struct workqueue_struct *glock_workqueue; -struct workqueue_struct *gfs2_delete_workqueue; static LIST_HEAD(lru_list); static atomic_t lru_count = ATOMIC_INIT(0); static DEFINE_SPINLOCK(lru_lock); @@ -274,9 +273,8 @@ static void __gfs2_glock_put(struct gfs2_glock *gl) struct address_space *mapping = gfs2_glock2aspace(gl); lockref_mark_dead(&gl->gl_lockref); - - gfs2_glock_remove_from_lru(gl); spin_unlock(&gl->gl_lockref.lock); + gfs2_glock_remove_from_lru(gl); GLOCK_BUG_ON(gl, !list_empty(&gl->gl_holders)); if (mapping) { truncate_inode_pages_final(mapping); @@ -883,6 +881,7 @@ void glock_set_object(struct gfs2_glock *gl, void *object) /** * glock_clear_object - clear the gl_object field of a glock * @gl: the glock + * @object: object the glock currently points at */ void glock_clear_object(struct gfs2_glock *gl, void *object) { @@ -892,8 +891,7 @@ void glock_clear_object(struct gfs2_glock *gl, void *object) prev_object = gl->gl_object; gl->gl_object = NULL; spin_unlock(&gl->gl_lockref.lock); - if (gfs2_assert_warn(gl->gl_name.ln_sbd, - prev_object == object || prev_object == NULL)) { + if (gfs2_assert_warn(gl->gl_name.ln_sbd, prev_object == object)) { pr_warn("glock=%u/%llx\n", gl->gl_name.ln_type, (unsigned long long)gl->gl_name.ln_number); @@ -977,6 +975,26 @@ static bool gfs2_try_evict(struct gfs2_glock *gl) return evicted; } +bool gfs2_queue_try_to_evict(struct gfs2_glock *gl) +{ + struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + + if (test_and_set_bit(GLF_TRY_TO_EVICT, &gl->gl_flags)) + return false; + return queue_delayed_work(sdp->sd_delete_wq, + &gl->gl_delete, 0); +} + +static bool gfs2_queue_verify_evict(struct gfs2_glock *gl) +{ + struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + + if (test_and_set_bit(GLF_VERIFY_EVICT, &gl->gl_flags)) + return false; + return queue_delayed_work(sdp->sd_delete_wq, + &gl->gl_delete, 5 * HZ); +} + static void delete_work_func(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); @@ -985,11 +1003,7 @@ static void delete_work_func(struct work_struct *work) struct inode *inode; u64 no_addr = gl->gl_name.ln_number; - spin_lock(&gl->gl_lockref.lock); - clear_bit(GLF_PENDING_DELETE, &gl->gl_flags); - spin_unlock(&gl->gl_lockref.lock); - - if (test_bit(GLF_DEMOTE, &gl->gl_flags)) { + if (test_and_clear_bit(GLF_TRY_TO_EVICT, &gl->gl_flags)) { /* * If we can evict the inode, give the remote node trying to * delete the inode some time before verifying that the delete @@ -1008,22 +1022,28 @@ static void delete_work_func(struct work_struct *work) * step entirely. */ if (gfs2_try_evict(gl)) { - if (gfs2_queue_delete_work(gl, 5 * HZ)) + if (test_bit(SDF_DEACTIVATING, &sdp->sd_flags)) + goto out; + if (gfs2_queue_verify_evict(gl)) return; } goto out; } - inode = gfs2_lookup_by_inum(sdp, no_addr, gl->gl_no_formal_ino, - GFS2_BLKST_UNLINKED); - if (IS_ERR(inode)) { - if (PTR_ERR(inode) == -EAGAIN && - (gfs2_queue_delete_work(gl, 5 * HZ))) + if (test_and_clear_bit(GLF_VERIFY_EVICT, &gl->gl_flags)) { + inode = gfs2_lookup_by_inum(sdp, no_addr, gl->gl_no_formal_ino, + GFS2_BLKST_UNLINKED); + if (IS_ERR(inode)) { + if (PTR_ERR(inode) == -EAGAIN && + !test_bit(SDF_DEACTIVATING, &sdp->sd_flags) && + gfs2_queue_verify_evict(gl)) return; - } else { - d_prune_aliases(inode); - iput(inode); + } else { + d_prune_aliases(inode); + iput(inode); + } } + out: gfs2_glock_put(gl); } @@ -1985,26 +2005,26 @@ add_back_to_lru: static long gfs2_scan_glock_lru(int nr) { - struct gfs2_glock *gl; - LIST_HEAD(skipped); + struct gfs2_glock *gl, *next; LIST_HEAD(dispose); long freed = 0; spin_lock(&lru_lock); - while ((nr-- >= 0) && !list_empty(&lru_list)) { - gl = list_first_entry(&lru_list, struct gfs2_glock, gl_lru); - + list_for_each_entry_safe(gl, next, &lru_list, gl_lru) { + if (nr-- <= 0) + break; /* Test for being demotable */ if (!test_bit(GLF_LOCK, &gl->gl_flags)) { - list_move(&gl->gl_lru, &dispose); - atomic_dec(&lru_count); - freed++; - continue; + if (!spin_trylock(&gl->gl_lockref.lock)) + continue; + if (!gl->gl_lockref.count) { + list_move(&gl->gl_lru, &dispose); + atomic_dec(&lru_count); + freed++; + } + spin_unlock(&gl->gl_lockref.lock); } - - list_move(&gl->gl_lru, &skipped); } - list_splice(&skipped, &lru_list); if (!list_empty(&dispose)) gfs2_dispose_glock_lru(&dispose); spin_unlock(&lru_lock); @@ -2063,37 +2083,21 @@ static void glock_hash_walk(glock_examiner examiner, const struct gfs2_sbd *sdp) rhashtable_walk_exit(&iter); } -bool gfs2_queue_delete_work(struct gfs2_glock *gl, unsigned long delay) -{ - bool queued; - - spin_lock(&gl->gl_lockref.lock); - queued = queue_delayed_work(gfs2_delete_workqueue, - &gl->gl_delete, delay); - if (queued) - set_bit(GLF_PENDING_DELETE, &gl->gl_flags); - spin_unlock(&gl->gl_lockref.lock); - return queued; -} - void gfs2_cancel_delete_work(struct gfs2_glock *gl) { - if (cancel_delayed_work(&gl->gl_delete)) { - clear_bit(GLF_PENDING_DELETE, &gl->gl_flags); + clear_bit(GLF_TRY_TO_EVICT, &gl->gl_flags); + clear_bit(GLF_VERIFY_EVICT, &gl->gl_flags); + if (cancel_delayed_work(&gl->gl_delete)) gfs2_glock_put(gl); - } -} - -bool gfs2_delete_work_queued(const struct gfs2_glock *gl) -{ - return test_bit(GLF_PENDING_DELETE, &gl->gl_flags); } static void flush_delete_work(struct gfs2_glock *gl) { if (gl->gl_name.ln_type == LM_TYPE_IOPEN) { + struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + if (cancel_delayed_work(&gl->gl_delete)) { - queue_delayed_work(gfs2_delete_workqueue, + queue_delayed_work(sdp->sd_delete_wq, &gl->gl_delete, 0); } } @@ -2102,7 +2106,7 @@ static void flush_delete_work(struct gfs2_glock *gl) void gfs2_flush_delete_work(struct gfs2_sbd *sdp) { glock_hash_walk(flush_delete_work, sdp); - flush_workqueue(gfs2_delete_workqueue); + flush_workqueue(sdp->sd_delete_wq); } /** @@ -2308,14 +2312,16 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl) *p++ = 'o'; if (test_bit(GLF_BLOCKING, gflags)) *p++ = 'b'; - if (test_bit(GLF_PENDING_DELETE, gflags)) - *p++ = 'P'; if (test_bit(GLF_FREEING, gflags)) *p++ = 'x'; if (test_bit(GLF_INSTANTIATE_NEEDED, gflags)) *p++ = 'n'; if (test_bit(GLF_INSTANTIATE_IN_PROG, gflags)) *p++ = 'N'; + if (test_bit(GLF_TRY_TO_EVICT, gflags)) + *p++ = 'e'; + if (test_bit(GLF_VERIFY_EVICT, gflags)) + *p++ = 'E'; *p = 0; return buf; } @@ -2465,18 +2471,9 @@ int __init gfs2_glock_init(void) rhashtable_destroy(&gl_hash_table); return -ENOMEM; } - gfs2_delete_workqueue = alloc_workqueue("delete_workqueue", - WQ_MEM_RECLAIM | WQ_FREEZABLE, - 0); - if (!gfs2_delete_workqueue) { - destroy_workqueue(glock_workqueue); - rhashtable_destroy(&gl_hash_table); - return -ENOMEM; - } ret = register_shrinker(&glock_shrinker, "gfs2-glock"); if (ret) { - destroy_workqueue(gfs2_delete_workqueue); destroy_workqueue(glock_workqueue); rhashtable_destroy(&gl_hash_table); return ret; @@ -2493,7 +2490,6 @@ void gfs2_glock_exit(void) unregister_shrinker(&glock_shrinker); rhashtable_destroy(&gl_hash_table); destroy_workqueue(glock_workqueue); - destroy_workqueue(gfs2_delete_workqueue); } static void gfs2_glock_iter_next(struct gfs2_glock_iter *gi, loff_t n) diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index f37ac087e2c1..1f1ba92c15a8 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -144,7 +144,6 @@ struct gfs2_glock_aspace { struct address_space mapping; }; -extern struct workqueue_struct *gfs2_delete_workqueue; static inline struct gfs2_holder *gfs2_glock_is_locked_by_me(struct gfs2_glock *gl) { struct gfs2_holder *gh; @@ -268,9 +267,8 @@ static inline int gfs2_glock_nq_init(struct gfs2_glock *gl, extern void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state); extern void gfs2_glock_complete(struct gfs2_glock *gl, int ret); -extern bool gfs2_queue_delete_work(struct gfs2_glock *gl, unsigned long delay); +extern bool gfs2_queue_try_to_evict(struct gfs2_glock *gl); extern void gfs2_cancel_delete_work(struct gfs2_glock *gl); -extern bool gfs2_delete_work_queued(const struct gfs2_glock *gl); extern void gfs2_flush_delete_work(struct gfs2_sbd *sdp); extern void gfs2_gl_hash_clear(struct gfs2_sbd *sdp); extern void gfs2_gl_dq_holders(struct gfs2_sbd *sdp); diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index d78b61ecc1cd..ad14818a790a 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -193,7 +193,7 @@ static int rgrp_go_sync(struct gfs2_glock *gl) struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl); int error; - if (!test_and_clear_bit(GLF_DIRTY, &gl->gl_flags)) + if (!rgd || !test_and_clear_bit(GLF_DIRTY, &gl->gl_flags)) return 0; GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE); @@ -222,9 +222,12 @@ static void rgrp_go_inval(struct gfs2_glock *gl, int flags) struct address_space *mapping = &sdp->sd_aspace; struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl); const unsigned bsize = sdp->sd_sb.sb_bsize; - loff_t start = (rgd->rd_addr * bsize) & PAGE_MASK; - loff_t end = PAGE_ALIGN((rgd->rd_addr + rgd->rd_length) * bsize) - 1; + loff_t start, end; + if (!rgd) + return; + start = (rgd->rd_addr * bsize) & PAGE_MASK; + end = PAGE_ALIGN((rgd->rd_addr + rgd->rd_length) * bsize) - 1; gfs2_rgrp_brelse(rgd); WARN_ON_ONCE(!(flags & DIO_METADATA)); truncate_inode_pages_range(mapping, start, end); @@ -645,23 +648,18 @@ static void iopen_go_callback(struct gfs2_glock *gl, bool remote) struct gfs2_inode *ip = gl->gl_object; struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; - if (!remote || sb_rdonly(sdp->sd_vfs)) + if (!remote || sb_rdonly(sdp->sd_vfs) || + test_bit(SDF_DEACTIVATING, &sdp->sd_flags)) return; if (gl->gl_demote_state == LM_ST_UNLOCKED && gl->gl_state == LM_ST_SHARED && ip) { gl->gl_lockref.count++; - if (!queue_delayed_work(gfs2_delete_workqueue, - &gl->gl_delete, 0)) + if (!gfs2_queue_try_to_evict(gl)) gl->gl_lockref.count--; } } -static int iopen_go_demote_ok(const struct gfs2_glock *gl) -{ - return !gfs2_delete_work_queued(gl); -} - /** * inode_go_free - wake up anyone waiting for dlm's unlock ast to free it * @gl: glock being freed @@ -767,7 +765,6 @@ const struct gfs2_glock_operations gfs2_iopen_glops = { .go_type = LM_TYPE_IOPEN, .go_callback = iopen_go_callback, .go_dump = inode_go_dump, - .go_demote_ok = iopen_go_demote_ok, .go_flags = GLOF_LRU | GLOF_NONDISK, .go_subclass = 1, }; diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index c26765080f28..79485329118b 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -329,8 +329,9 @@ enum { GLF_LRU = 13, GLF_OBJECT = 14, /* Used only for tracing */ GLF_BLOCKING = 15, - GLF_PENDING_DELETE = 17, - GLF_FREEING = 18, /* Wait for glock to be freed */ + GLF_FREEING = 16, /* Wait for glock to be freed */ + GLF_TRY_TO_EVICT = 17, /* iopen glocks only */ + GLF_VERIFY_EVICT = 18, /* iopen glocks only */ }; struct gfs2_glock { @@ -605,6 +606,8 @@ enum { SDF_REMOTE_WITHDRAW = 13, /* Performing remote recovery */ SDF_WITHDRAW_RECOVERY = 14, /* Wait for journal recovery when we are withdrawing */ + SDF_DEACTIVATING = 15, + SDF_EVICTING = 16, }; enum gfs2_freeze_state { @@ -771,6 +774,10 @@ struct gfs2_sbd { struct completion sd_journal_ready; + /* Workqueue stuff */ + + struct workqueue_struct *sd_delete_wq; + /* Daemon stuff */ struct task_struct *sd_logd_process; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 713efa3bb732..1291b5ee3584 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -225,6 +225,10 @@ fail: gfs2_glock_dq_uninit(&ip->i_iopen_gh); if (gfs2_holder_initialized(&i_gh)) gfs2_glock_dq_uninit(&i_gh); + if (ip->i_gl) { + gfs2_glock_put(ip->i_gl); + ip->i_gl = NULL; + } iget_failed(inode); return ERR_PTR(error); } @@ -816,6 +820,10 @@ fail_gunlock3: fail_gunlock2: gfs2_glock_put(io_gl); fail_free_inode: + if (ip->i_gl) { + gfs2_glock_put(ip->i_gl); + ip->i_gl = NULL; + } gfs2_rs_deltree(&ip->i_res); gfs2_qa_put(ip); fail_free_acls: diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index c0cf1d2d0ef5..6de901c3b89b 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1197,9 +1197,15 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) snprintf(sdp->sd_fsname, sizeof(sdp->sd_fsname), "%s", sdp->sd_table_name); + sdp->sd_delete_wq = alloc_workqueue("gfs2-delete/%s", + WQ_MEM_RECLAIM | WQ_FREEZABLE, 0, sdp->sd_fsname); + error = -ENOMEM; + if (!sdp->sd_delete_wq) + goto fail_free; + error = gfs2_sys_fs_add(sdp); if (error) - goto fail_free; + goto fail_delete_wq; gfs2_create_debugfs_file(sdp); @@ -1309,6 +1315,8 @@ fail_lm: fail_debug: gfs2_delete_debugfs_file(sdp); gfs2_sys_fs_del(sdp); +fail_delete_wq: + destroy_workqueue(sdp->sd_delete_wq); fail_free: free_sbd(sdp); sb->s_fs_info = NULL; @@ -1720,6 +1728,55 @@ static int gfs2_meta_init_fs_context(struct fs_context *fc) return 0; } +/** + * gfs2_evict_inodes - evict inodes cooperatively + * @sb: the superblock + * + * When evicting an inode with a zero link count, we are trying to upgrade the + * inode's iopen glock from SH to EX mode in order to determine if we can + * delete the inode. The other nodes are supposed to evict the inode from + * their caches if they can, and to poke the inode's inode glock if they cannot + * do so. Either behavior allows gfs2_upgrade_iopen_glock() to proceed + * quickly, but if the other nodes are not cooperating, the lock upgrading + * attempt will time out. Since inodes are evicted sequentially, this can add + * up quickly. + * + * Function evict_inodes() tries to keep the s_inode_list_lock list locked over + * a long time, which prevents other inodes from being evicted concurrently. + * This precludes the cooperative behavior we are looking for. This special + * version of evict_inodes() avoids that. + * + * Modeled after drop_pagecache_sb(). + */ +static void gfs2_evict_inodes(struct super_block *sb) +{ + struct inode *inode, *toput_inode = NULL; + struct gfs2_sbd *sdp = sb->s_fs_info; + + set_bit(SDF_EVICTING, &sdp->sd_flags); + + spin_lock(&sb->s_inode_list_lock); + list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { + spin_lock(&inode->i_lock); + if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) && + !need_resched()) { + spin_unlock(&inode->i_lock); + continue; + } + atomic_inc(&inode->i_count); + spin_unlock(&inode->i_lock); + spin_unlock(&sb->s_inode_list_lock); + + iput(toput_inode); + toput_inode = inode; + + cond_resched(); + spin_lock(&sb->s_inode_list_lock); + } + spin_unlock(&sb->s_inode_list_lock); + iput(toput_inode); +} + static void gfs2_kill_sb(struct super_block *sb) { struct gfs2_sbd *sdp = sb->s_fs_info; @@ -1735,6 +1792,18 @@ static void gfs2_kill_sb(struct super_block *sb) sdp->sd_root_dir = NULL; sdp->sd_master_dir = NULL; shrink_dcache_sb(sb); + + gfs2_evict_inodes(sb); + + /* + * Flush and then drain the delete workqueue here (via + * destroy_workqueue()) to ensure that any delete work that + * may be running will also see the SDF_DEACTIVATING flag. + */ + set_bit(SDF_DEACTIVATING, &sdp->sd_flags); + gfs2_flush_delete_work(sdp); + destroy_workqueue(sdp->sd_delete_wq); + kill_block_super(sb); } diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index f602fb844951..3b9b76e980ad 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1879,7 +1879,7 @@ static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, u64 skip */ ip = gl->gl_object; - if (ip || !gfs2_queue_delete_work(gl, 0)) + if (ip || !gfs2_queue_try_to_evict(gl)) gfs2_glock_put(gl); else found++; diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 999cc146d708..a83fa62106f0 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -138,8 +138,10 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp) return -EIO; error = gfs2_find_jhead(sdp->sd_jdesc, &head, false); - if (error || gfs2_withdrawn(sdp)) + if (error) { + gfs2_consist(sdp); return error; + } if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) { gfs2_consist(sdp); @@ -151,7 +153,9 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp) gfs2_log_pointers_init(sdp, head.lh_blkno); error = gfs2_quota_init(sdp); - if (!error && !gfs2_withdrawn(sdp)) + if (!error && gfs2_withdrawn(sdp)) + error = -EIO; + if (!error) set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); return error; } @@ -529,7 +533,9 @@ void gfs2_make_fs_ro(struct gfs2_sbd *sdp) { int log_write_allowed = test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); - gfs2_flush_delete_work(sdp); + if (!test_bit(SDF_DEACTIVATING, &sdp->sd_flags)) + gfs2_flush_delete_work(sdp); + if (!log_write_allowed && current == sdp->sd_quotad_process) fs_warn(sdp, "The quotad daemon is withdrawing.\n"); else if (sdp->sd_quotad_process) @@ -933,6 +939,7 @@ static int gfs2_statfs(struct dentry *dentry, struct kstatfs *buf) static int gfs2_drop_inode(struct inode *inode) { struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); if (inode->i_nlink && gfs2_holder_initialized(&ip->i_iopen_gh)) { @@ -952,11 +959,17 @@ static int gfs2_drop_inode(struct inode *inode) struct gfs2_glock *gl = ip->i_iopen_gh.gh_gl; gfs2_glock_hold(gl); - if (!gfs2_queue_delete_work(gl, 0)) + if (!gfs2_queue_try_to_evict(gl)) gfs2_glock_queue_put(gl); return 0; } + /* + * No longer cache inodes when trying to evict them all. + */ + if (test_bit(SDF_EVICTING, &sdp->sd_flags)) + return 1; + return generic_drop_inode(inode); } @@ -1175,15 +1188,23 @@ static bool gfs2_upgrade_iopen_glock(struct inode *inode) gfs2_glock_dq_wait(gh); /* - * If there are no other lock holders, we'll get the lock immediately. + * If there are no other lock holders, we will immediately get + * exclusive access to the iopen glock here. + * * Otherwise, the other nodes holding the lock will be notified about - * our locking request. If they don't have the inode open, they'll - * evict the cached inode and release the lock. Otherwise, if they - * poke the inode glock, we'll take this as an indication that they - * still need the iopen glock and that they'll take care of deleting - * the inode when they're done. As a last resort, if another node - * keeps holding the iopen glock without showing any activity on the - * inode glock, we'll eventually time out. + * our locking request. If they do not have the inode open, they are + * expected to evict the cached inode and release the lock, allowing us + * to proceed. + * + * Otherwise, if they cannot evict the inode, they are expected to poke + * the inode glock (note: not the iopen glock). We will notice that + * and stop waiting for the iopen glock immediately. The other node(s) + * are then expected to take care of deleting the inode when they no + * longer use it. + * + * As a last resort, if another node keeps holding the iopen glock + * without showing any activity on the inode glock, we will eventually + * time out and fail the iopen glock upgrade. * * Note that we're passing the LM_FLAG_TRY_1CB flag to the first * locking request as an optimization to notify lock holders as soon as @@ -1401,10 +1422,8 @@ static void gfs2_evict_inode(struct inode *inode) if (gfs2_rs_active(&ip->i_res)) gfs2_rs_deltree(&ip->i_res); - if (gfs2_holder_initialized(&gh)) { - glock_clear_object(ip->i_gl, ip); + if (gfs2_holder_initialized(&gh)) gfs2_glock_dq_uninit(&gh); - } if (ret && ret != GLR_TRYFAILED && ret != -EROFS) fs_warn(sdp, "gfs2_evict_inode: %d\n", ret); out: diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index d87ea98cf535..c40118ea4bbc 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -87,6 +87,7 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf) "Withdraw In Prog: %d\n" "Remote Withdraw: %d\n" "Withdraw Recovery: %d\n" + "Deactivating: %d\n" "sd_log_error: %d\n" "sd_log_flush_lock: %d\n" "sd_log_num_revoke: %u\n" @@ -115,6 +116,7 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf) test_bit(SDF_WITHDRAW_IN_PROG, &f), test_bit(SDF_REMOTE_WITHDRAW, &f), test_bit(SDF_WITHDRAW_RECOVERY, &f), + test_bit(SDF_DEACTIVATING, &f), sdp->sd_log_error, rwsem_is_locked(&sdp->sd_log_flush_lock), sdp->sd_log_num_revoke, |