Skip to content

Commit

Permalink
Merge tag 'gfs2-v6.2-rc5-fixes' of git://git.kernel.org/pub/scm/linux…
Browse files Browse the repository at this point in the history
…/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
  • Loading branch information
torvalds committed Feb 22, 2023
2 parents 28e3352 + c1b0c3c commit b7ee881
Show file tree
Hide file tree
Showing 13 changed files with 204 additions and 127 deletions.
9 changes: 4 additions & 5 deletions fs/gfs2/aops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -127,15 +127,14 @@ 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);
if (!page_has_buffers(page)) {
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);
}
Expand Down
4 changes: 2 additions & 2 deletions fs/gfs2/aops.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__ */
4 changes: 2 additions & 2 deletions fs/gfs2/bmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 0 additions & 18 deletions fs/gfs2/dentry.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

128 changes: 62 additions & 66 deletions fs/gfs2/glock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
{
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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);
}
Expand Down Expand Up @@ -1985,26 +2005,26 @@ __acquires(&lru_lock)

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);
Expand Down Expand Up @@ -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);
}
}
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down
4 changes: 1 addition & 3 deletions fs/gfs2/glock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit b7ee881

Please sign in to comment.