gfs2 fixes

- Fix a race when disassociating inodes from their glocks after
   iget_failed().
 
 - On filesystems with a block size smaller than the page size, make
   sure that ->writepages() writes out all buffers of journaled inodes.
 
 - Various improvements to the way the delete workqueue is drained to
   speed up unmount and prevent leftover inodes.  At unmount time, evict
   deleted inodes cooperatively across the cluster to avoid unnecessary
   timeouts.
 
 - Various minor cleanups and fixes.
 -----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCAAyFiEEJZs3krPW0xkhLMTc1b+f6wMTZToFAmP2ASMUHGFncnVlbmJh
 QHJlZGhhdC5jb20ACgkQ1b+f6wMTZTodnQ//XSt/sKo6W4y56Pxz5AHO2cTdypLk
 b+ki1uWfguBm/3o8nFtXedoCcTsWZ8EICPL9bZKFBwSLSRChZuc6kKDz07wwseKR
 t8Lbi9G8tSwyDeQaiGTBd1UBWFAjNGqXniyqx/ki+RZZ3QMVJnwcb1Bjtl9DJ5DE
 lAXcvz0DZSQQwtpsdG+qpme65XZSziS0uDgkaz5Pio/1NDfbZ/U28HBMLNkS/Ef7
 RnF05PRaM07OGn2rXmvIqwIwoxjH2eF38x5EyMI3xpr//b3x/mVbwL+QFECfgf7r
 iuISCtL46n/gs4NmroPfT5LbCDhkOw513mmkdJNKXwHsz4s8hS+BuZVtTa8b02hn
 0K5Ova63qz3TIoZ+P6n44xiRFEVjqz0eqn0XhOr+HRljRXhn2ihxQLl4yGNgrmB2
 KTC+xMqZHXs2J3H97OTZDJVHYe5k6HqzvBUN6BnGRZ1lSEJbt5Fe0b7Web2aaLY2
 X2jFXplWVisTPKcusSuG3kP4WrEJq7ic8YLX6BgKU5DBbS69NETssUuMGUIxsd6k
 P+A4wfrUWac+X+DHFRPJu1yNL2UsW237AX75sNNOqLNRX04ZjXGFxymnEw//t2Qr
 2sPOEkU4O61o7tYWlK8PXTDVEbteZO3pBCdj7ARsmDEY401QuT9ZlmpmhDxxP/hP
 TGLDXXMbG+eireU=
 =3v/p
 -----END PGP SIGNATURE-----

Merge tag 'gfs2-v6.2-rc5-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2

Pull gfs2 updates from Andreas Gruenbacher:

 - Fix a race when disassociating inodes from their glocks after
   iget_failed()

 - On filesystems with a block size smaller than the page size, make
   sure that ->writepages() writes out all buffers of journaled inodes

 - Various improvements to the way the delete workqueue is drained to
   speed up unmount and prevent leftover inodes. At unmount time, evict
   deleted inodes cooperatively across the cluster to avoid unnecessary
   timeouts

 - Various minor cleanups and fixes

* tag 'gfs2-v6.2-rc5-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
  gfs2: Convert gfs2_page_add_databufs to folios
  gfs2: jdata writepage fix
  gfs2: Improve gfs2_make_fs_rw error handling
  Revert "GFS2: free disk inode which is deleted by remote node -V2"
  gfs2: Evict inodes cooperatively
  gfs2: Flush delete work before shrinking inode cache
  gfs2: Cease delete work during unmount
  gfs2: Add SDF_DEACTIVATING super block flag
  gfs2: check gl_object in rgrp glops
  gfs2: Split the two kinds of glock "delete" work
  gfs2: Move delete workqueue into super block
  gfs2: Get rid of GLF_PENDING_DELETE flag
  gfs2: Make glock lru list scanning safer
  gfs2: Clean up gfs2_scan_glock_lru
  gfs2: Improve gfs2_upgrade_iopen_glock comment
  gfs2: gl_object races fix
This commit is contained in:
Linus Torvalds 2023-02-22 14:00:53 -08:00
commit b7ee881282
13 changed files with 204 additions and 127 deletions

View File

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

View File

@ -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__ */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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