* [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups"
@ 2024-01-19 21:20 Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 1/9] gfs2: Fix gfs2_drevalidate NULL pointer dereference Andreas Gruenbacher
` (11 more replies)
0 siblings, 12 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-01-19 21:20 UTC (permalink / raw)
To: gfs2; +Cc: Al Viro, Andreas Gruenbacher
Hello,
Al Viro has reported issues with commit dd00aaeb3432 ("gfs2: Use
GL_NOBLOCK flag for non-blocking lookups"):
* First, parent can now be NULL and dereferencing it in
gfs2_dir_check(d_inode(parent), &dentry->d_name, ip) isn't going to
work;
* Second, gfs2_dir_check() can still sleep, which breaks LOOKUP_RCU
mode.
These patches should fix both of these problems. We'll either need
these fixes added, or commit dd00aaeb3432 reverted for now.
Thanks,
Andreas
Andreas Gruenbacher (9):
gfs2: Fix gfs2_drevalidate NULL pointer dereference
gfs2: Pass FGP flags to gfs2_getbuf
gfs2: Split gfs2_meta_read_async off from gfs2_meta_read
gfs2: Add FGP_NOWAIT support to gfs2_meta_read_async
gfs2: Pass FGP flags to gfs2_meta_{,inode_}buffer
gfs2: Pass FGP flags to gfs2_dirent_search
gfs2: Pass FGP flags to gfs2_dir_check
gfs2: Minor gfs2_drevalidate cleanup
gfs2: Fix LOOKUP_RCU support in gfs2_drevalidate
fs/gfs2/aops.c | 4 +-
fs/gfs2/bmap.c | 21 ++++----
fs/gfs2/dentry.c | 32 ++++++++-----
fs/gfs2/dir.c | 52 +++++++++++---------
fs/gfs2/dir.h | 2 +-
fs/gfs2/file.c | 4 +-
fs/gfs2/glops.c | 2 +-
fs/gfs2/incore.h | 1 -
fs/gfs2/inode.c | 10 ++--
fs/gfs2/meta_io.c | 112 ++++++++++++++++++++++++++++---------------
fs/gfs2/meta_io.h | 15 +++---
fs/gfs2/ops_fstype.c | 4 +-
fs/gfs2/quota.c | 2 +-
fs/gfs2/recovery.c | 2 +-
fs/gfs2/rgrp.c | 5 +-
fs/gfs2/super.c | 6 +--
fs/gfs2/xattr.c | 17 ++++---
17 files changed, 173 insertions(+), 118 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 1/9] gfs2: Fix gfs2_drevalidate NULL pointer dereference
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
@ 2024-01-19 21:20 ` Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 2/9] gfs2: Pass FGP flags to gfs2_getbuf Andreas Gruenbacher
` (10 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-01-19 21:20 UTC (permalink / raw)
To: gfs2; +Cc: Al Viro, Andreas Gruenbacher
Commit dd00aaeb3432 added an RCU-safe way of computing d_inode(parent)
to gfs2_drevalidate() to support the LOOKUP_RCU flag, but then failed to
convert one of the instances of d_inode(parent) to its RCU-safe
replacement. This manifested as a NULL pointer dereference. Fix that.
Reported-by: Al Viro <viro@zeniv.linux.org.uk>
Fixes: dd00aaeb3432 ("gfs2: Use GL_NOBLOCK flag for non-blocking lookups")
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/dentry.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c
index 177f1f41f225..c6483fb98624 100644
--- a/fs/gfs2/dentry.c
+++ b/fs/gfs2/dentry.c
@@ -72,7 +72,7 @@ static int gfs2_drevalidate(struct dentry *dentry, unsigned int flags)
goto out;
}
- error = gfs2_dir_check(d_inode(parent), &dentry->d_name, ip);
+ error = gfs2_dir_check(dinode, &dentry->d_name, ip);
valid = inode ? !error : (error == -ENOENT);
if (!had_lock)
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 2/9] gfs2: Pass FGP flags to gfs2_getbuf
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 1/9] gfs2: Fix gfs2_drevalidate NULL pointer dereference Andreas Gruenbacher
@ 2024-01-19 21:20 ` Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 3/9] gfs2: Split gfs2_meta_read_async off from gfs2_meta_read Andreas Gruenbacher
` (9 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-01-19 21:20 UTC (permalink / raw)
To: gfs2; +Cc: Al Viro, Andreas Gruenbacher
Replace gfs2_getbuf()'s create argument with a fgp_flags argument. Use
the FGP_CREAT flag instead of the CREATE flag to indicate that new
buffers should be created.
In addition, when the FGP_NOWAIT flag is set and gfs2_getbuf() would
sleep, -EAGAIN is returned instead.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/bmap.c | 2 +-
fs/gfs2/dir.c | 2 +-
fs/gfs2/meta_io.c | 33 +++++++++++++++++++--------------
fs/gfs2/meta_io.h | 2 +-
4 files changed, 22 insertions(+), 17 deletions(-)
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index d9ccfd27e4f1..92945e5b7643 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -301,7 +301,7 @@ static void gfs2_metapath_ra(struct gfs2_glock *gl, __be64 *start, __be64 *end)
if (!*t)
continue;
- rabh = gfs2_getbuf(gl, be64_to_cpu(*t), CREATE);
+ rabh = gfs2_getbuf(gl, be64_to_cpu(*t), FGP_CREAT);
if (trylock_buffer(rabh)) {
if (!buffer_uptodate(rabh)) {
rabh->b_end_io = end_buffer_read_sync;
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 560e4624c09f..518a7fb42df0 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -1500,7 +1500,7 @@ static void gfs2_dir_readahead(struct inode *inode, unsigned hsize, u32 index,
if (blocknr == last)
continue;
- bh = gfs2_getbuf(gl, blocknr, 1);
+ bh = gfs2_getbuf(gl, blocknr, FGP_CREAT);
if (trylock_buffer(bh)) {
if (buffer_uptodate(bh)) {
unlock_buffer(bh);
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index f814054c8cd0..d0f3727f24db 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -106,12 +106,15 @@ const struct address_space_operations gfs2_rgrp_aops = {
* gfs2_getbuf - Get a buffer with a given address space
* @gl: the glock
* @blkno: the block number (filesystem scope)
- * @create: 1 if the buffer should be created
+ * @fgp_flags: Flags like FGP_CREAT and FGP_NOWAIT
+ *
+ * Returns ERR_PTR(-EAGAIN) if the FGP_NOWAIT flag is set and the function
+ * would sleep.
*
* Returns: the buffer
*/
-
-struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno, int create)
+struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno,
+ fgf_t fgp_flags)
{
struct address_space *mapping = gfs2_glock2aspace(gl);
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
@@ -128,17 +131,19 @@ struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno, int create)
index = blkno >> shift; /* convert block to page */
bufnum = blkno - (index << shift); /* block buf index within page */
- if (create) {
+ fgp_flags |= FGP_LOCK | FGP_ACCESSED;
+ if (fgp_flags & FGP_CREAT) {
folio = __filemap_get_folio(mapping, index,
- FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
+ fgp_flags,
mapping_gfp_mask(mapping) | __GFP_NOFAIL);
+ if (IS_ERR(folio))
+ return ERR_CAST(folio);
bh = folio_buffers(folio);
- if (!bh)
+ if (!bh && !(fgp_flags & FGP_NOWAIT))
bh = create_empty_buffers(folio,
sdp->sd_sb.sb_bsize, 0);
} else {
- folio = __filemap_get_folio(mapping, index,
- FGP_LOCK | FGP_ACCESSED, 0);
+ folio = __filemap_get_folio(mapping, index, fgp_flags, 0);
if (IS_ERR(folio))
return NULL;
bh = folio_buffers(folio);
@@ -181,7 +186,7 @@ static void meta_prep_new(struct buffer_head *bh)
struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno)
{
struct buffer_head *bh;
- bh = gfs2_getbuf(gl, blkno, CREATE);
+ bh = gfs2_getbuf(gl, blkno, FGP_CREAT);
meta_prep_new(bh);
return bh;
}
@@ -258,7 +263,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
return -EIO;
}
- *bhp = bh = gfs2_getbuf(gl, blkno, CREATE);
+ *bhp = bh = gfs2_getbuf(gl, blkno, FGP_CREAT);
lock_buffer(bh);
if (buffer_uptodate(bh)) {
@@ -271,7 +276,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
}
if (rahead) {
- bh = gfs2_getbuf(gl, blkno + 1, CREATE);
+ bh = gfs2_getbuf(gl, blkno + 1, FGP_CREAT);
lock_buffer(bh);
if (buffer_uptodate(bh)) {
@@ -443,7 +448,7 @@ void gfs2_journal_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen)
gfs2_ail1_wipe(sdp, bstart, blen);
while (blen) {
ty = REMOVE_META;
- bh = gfs2_getbuf(ip->i_gl, bstart, NO_CREATE);
+ bh = gfs2_getbuf(ip->i_gl, bstart, 0);
if (!bh && gfs2_is_jdata(ip)) {
bh = gfs2_getjdatabuf(ip, bstart);
ty = REMOVE_JDATA;
@@ -519,7 +524,7 @@ struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen)
if (extlen > max_ra)
extlen = max_ra;
- first_bh = gfs2_getbuf(gl, dblock, CREATE);
+ first_bh = gfs2_getbuf(gl, dblock, FGP_CREAT);
if (buffer_uptodate(first_bh))
goto out;
@@ -529,7 +534,7 @@ struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen)
extlen--;
while (extlen) {
- bh = gfs2_getbuf(gl, dblock, CREATE);
+ bh = gfs2_getbuf(gl, dblock, FGP_CREAT);
bh_readahead(bh, REQ_RAHEAD | REQ_META | REQ_PRIO);
brelse(bh);
diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h
index 831d988c2ceb..e239e410881c 100644
--- a/fs/gfs2/meta_io.h
+++ b/fs/gfs2/meta_io.h
@@ -55,7 +55,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
int rahead, struct buffer_head **bhp);
int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh);
struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno,
- int create);
+ fgf_t fgp_flags);
enum {
REMOVE_JDATA = 0,
REMOVE_META = 1,
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 3/9] gfs2: Split gfs2_meta_read_async off from gfs2_meta_read
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 1/9] gfs2: Fix gfs2_drevalidate NULL pointer dereference Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 2/9] gfs2: Pass FGP flags to gfs2_getbuf Andreas Gruenbacher
@ 2024-01-19 21:20 ` Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 4/9] gfs2: Add FGP_NOWAIT support to gfs2_meta_read_async Andreas Gruenbacher
` (8 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-01-19 21:20 UTC (permalink / raw)
To: gfs2; +Cc: Al Viro, Andreas Gruenbacher
Split gfs2_meta_read_async() off from gfs2_meta_read() and implement
gfs2_meta_read() in terms of gfs2_meta_read_async() and
gfs2_meta_wait().
The initial check for filesystem withdrawal in gfs2_meta_wait() is
unnecessary and can be removed because gfs2_meta_wait() always follows
gfs2_meta_read_async() in the code and gfs2_meta_read_async() already
performs that check.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/dir.c | 6 +++---
fs/gfs2/incore.h | 1 -
fs/gfs2/meta_io.c | 46 ++++++++++++++++++++++------------------------
fs/gfs2/meta_io.h | 6 ++++--
fs/gfs2/quota.c | 2 +-
fs/gfs2/rgrp.c | 3 ++-
fs/gfs2/xattr.c | 13 ++++++-------
7 files changed, 38 insertions(+), 39 deletions(-)
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 518a7fb42df0..446e374a8d78 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -105,7 +105,7 @@ static int gfs2_dir_get_existing_buffer(struct gfs2_inode *ip, u64 block,
struct buffer_head *bh;
int error;
- error = gfs2_meta_read(ip->i_gl, block, DIO_WAIT, 0, &bh);
+ error = gfs2_meta_read(ip->i_gl, block, 0, &bh);
if (error)
return error;
if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh, GFS2_METATYPE_JD)) {
@@ -300,7 +300,7 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, __be64 *buf,
BUG_ON(extlen < 1);
bh = gfs2_meta_ra(ip->i_gl, dblock, extlen);
} else {
- error = gfs2_meta_read(ip->i_gl, dblock, DIO_WAIT, 0, &bh);
+ error = gfs2_meta_read(ip->i_gl, dblock, 0, &bh);
if (error)
goto fail;
}
@@ -757,7 +757,7 @@ static int get_leaf(struct gfs2_inode *dip, u64 leaf_no,
{
int error;
- error = gfs2_meta_read(dip->i_gl, leaf_no, DIO_WAIT, 0, bhp);
+ error = gfs2_meta_read(dip->i_gl, leaf_no, 0, bhp);
if (!error && gfs2_metatype_check(GFS2_SB(&dip->i_inode), *bhp, GFS2_METATYPE_LF)) {
/* pr_info("block num=%llu\n", leaf_no); */
error = -EIO;
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 95a334d64da2..e1343fd0a5b1 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -22,7 +22,6 @@
#include <linux/rhashtable.h>
#include <linux/mutex.h>
-#define DIO_WAIT 0x00000010
#define DIO_METADATA 0x00000020
struct gfs2_log_operations;
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index d0f3727f24db..6492d000d366 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -240,18 +240,17 @@ static void gfs2_submit_bhs(blk_opf_t opf, struct buffer_head *bhs[], int num)
}
/**
- * gfs2_meta_read - Read a block from disk
+ * gfs2_meta_read_async - Read a block from disk
* @gl: The glock covering the block
* @blkno: The block number
- * @flags: flags
* @rahead: Do read-ahead
* @bhp: the place where the buffer is returned (NULL on failure)
*
* Returns: errno
*/
-int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
- int rahead, struct buffer_head **bhp)
+int gfs2_meta_read_async(struct gfs2_glock *gl, u64 blkno,
+ int rahead, struct buffer_head **bhp)
{
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
struct buffer_head *bh, *bhs[2];
@@ -268,7 +267,6 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
lock_buffer(bh);
if (buffer_uptodate(bh)) {
unlock_buffer(bh);
- flags &= ~DIO_WAIT;
} else {
bh->b_end_io = end_buffer_read_sync;
get_bh(bh);
@@ -289,20 +287,6 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
}
gfs2_submit_bhs(REQ_OP_READ | REQ_META | REQ_PRIO, bhs, num);
- if (!(flags & DIO_WAIT))
- return 0;
-
- bh = *bhp;
- wait_on_buffer(bh);
- if (unlikely(!buffer_uptodate(bh))) {
- struct gfs2_trans *tr = current->journal_info;
- if (tr && test_bit(TR_TOUCHED, &tr->tr_flags))
- gfs2_io_error_bh_wd(sdp, bh);
- brelse(bh);
- *bhp = NULL;
- return -EIO;
- }
-
return 0;
}
@@ -316,10 +300,6 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh)
{
- if (gfs2_withdrawing_or_withdrawn(sdp) &&
- !gfs2_withdraw_in_prog(sdp))
- return -EIO;
-
wait_on_buffer(bh);
if (!buffer_uptodate(bh)) {
@@ -335,6 +315,24 @@ int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh)
return 0;
}
+int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno,
+ int rahead, struct buffer_head **bhp)
+{
+ struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
+ int ret;
+
+ ret = gfs2_meta_read_async(gl, blkno, rahead, bhp);
+ if (ret)
+ return ret;
+
+ ret = gfs2_meta_wait(sdp, *bhp);
+ if (ret) {
+ brelse(*bhp);
+ *bhp = NULL;
+ }
+ return ret;
+}
+
void gfs2_remove_from_journal(struct buffer_head *bh, int meta)
{
struct address_space *mapping = bh->b_folio->mapping;
@@ -491,7 +489,7 @@ int gfs2_meta_buffer(struct gfs2_inode *ip, u32 mtype, u64 num,
if (num == ip->i_no_addr)
rahead = ip->i_rahead;
- ret = gfs2_meta_read(gl, num, DIO_WAIT, rahead, &bh);
+ ret = gfs2_meta_read(gl, num, rahead, &bh);
if (ret == 0 && gfs2_metatype_check(sdp, bh, mtype)) {
brelse(bh);
ret = -EIO;
diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h
index e239e410881c..f04c91eadfb4 100644
--- a/fs/gfs2/meta_io.h
+++ b/fs/gfs2/meta_io.h
@@ -51,9 +51,11 @@ static inline struct gfs2_sbd *gfs2_mapping2sbd(struct address_space *mapping)
}
struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno);
-int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
- int rahead, struct buffer_head **bhp);
+int gfs2_meta_read_async(struct gfs2_glock *gl, u64 blkno,
+ int rahead, struct buffer_head **bhp);
int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh);
+int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno,
+ int rahead, struct buffer_head **bhp);
struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno,
fgf_t fgp_flags);
enum {
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index aa9cf0102848..a68af8bdf7de 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -424,7 +424,7 @@ static int bh_get(struct gfs2_quota_data *qd)
goto fail;
error = gfs2_meta_read(ip->i_gl, iomap.addr >> inode->i_blkbits,
- DIO_WAIT, 0, &bh);
+ 0, &bh);
if (error)
goto fail;
error = -EIO;
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index 26d6c1eea559..fcef82c767e3 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -1210,7 +1210,8 @@ int gfs2_rgrp_go_instantiate(struct gfs2_glock *gl)
for (x = 0; x < length; x++) {
bi = rgd->rd_bits + x;
- error = gfs2_meta_read(gl, rgd->rd_addr + x, 0, 0, &bi->bi_bh);
+ error = gfs2_meta_read_async(gl, rgd->rd_addr + x, 0,
+ &bi->bi_bh);
if (error)
goto fail;
}
diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c
index 8c96ba6230d1..759347ec67af 100644
--- a/fs/gfs2/xattr.c
+++ b/fs/gfs2/xattr.c
@@ -128,7 +128,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
__be64 *eablk, *end;
int error;
- error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT, 0, &bh);
+ error = gfs2_meta_read(ip->i_gl, ip->i_eattr, 0, &bh);
if (error)
return error;
@@ -152,7 +152,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
break;
bn = be64_to_cpu(*eablk);
- error = gfs2_meta_read(ip->i_gl, bn, DIO_WAIT, 0, &eabh);
+ error = gfs2_meta_read(ip->i_gl, bn, 0, &eabh);
if (error)
break;
error = ea_foreach_i(ip, eabh, ea_call, data);
@@ -468,8 +468,8 @@ static int gfs2_iter_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
return -ENOMEM;
for (x = 0; x < nptrs; x++) {
- error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs), 0, 0,
- bh + x);
+ error = gfs2_meta_read_async(ip->i_gl, be64_to_cpu(*dataptrs),
+ 0, bh + x);
if (error) {
while (x--)
brelse(bh[x]);
@@ -976,8 +976,7 @@ static int ea_set_block(struct gfs2_inode *ip, struct gfs2_ea_request *er,
if (ip->i_diskflags & GFS2_DIF_EA_INDIRECT) {
__be64 *end;
- error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT, 0,
- &indbh);
+ error = gfs2_meta_read(ip->i_gl, ip->i_eattr, 0, &indbh);
if (error)
return error;
@@ -1279,7 +1278,7 @@ static int ea_dealloc_indirect(struct gfs2_inode *ip)
memset(&rlist, 0, sizeof(struct gfs2_rgrp_list));
- error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT, 0, &indbh);
+ error = gfs2_meta_read(ip->i_gl, ip->i_eattr, 0, &indbh);
if (error)
return error;
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 4/9] gfs2: Add FGP_NOWAIT support to gfs2_meta_read_async
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
` (2 preceding siblings ...)
2024-01-19 21:20 ` [PATCH 3/9] gfs2: Split gfs2_meta_read_async off from gfs2_meta_read Andreas Gruenbacher
@ 2024-01-19 21:20 ` Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 5/9] gfs2: Pass FGP flags to gfs2_meta_{,inode_}buffer Andreas Gruenbacher
` (7 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-01-19 21:20 UTC (permalink / raw)
To: gfs2; +Cc: Al Viro, Andreas Gruenbacher
Add an fgp_flags argument to gfs2_meta_read_async() and gfs2_meta_read()
that is passed through to gfs2_getbuf(). When the FGP_NOWAIT flag is
set and gfs2_meta_read_async() would sleep, -EAGAIN is returned instead.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/dir.c | 6 +++---
fs/gfs2/meta_io.c | 40 ++++++++++++++++++++++++++++++++++------
fs/gfs2/meta_io.h | 4 ++--
fs/gfs2/quota.c | 2 +-
fs/gfs2/rgrp.c | 2 +-
fs/gfs2/xattr.c | 10 +++++-----
6 files changed, 46 insertions(+), 18 deletions(-)
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 446e374a8d78..a1f47696a2ec 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -105,7 +105,7 @@ static int gfs2_dir_get_existing_buffer(struct gfs2_inode *ip, u64 block,
struct buffer_head *bh;
int error;
- error = gfs2_meta_read(ip->i_gl, block, 0, &bh);
+ error = gfs2_meta_read(ip->i_gl, block, 0, 0, &bh);
if (error)
return error;
if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh, GFS2_METATYPE_JD)) {
@@ -300,7 +300,7 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, __be64 *buf,
BUG_ON(extlen < 1);
bh = gfs2_meta_ra(ip->i_gl, dblock, extlen);
} else {
- error = gfs2_meta_read(ip->i_gl, dblock, 0, &bh);
+ error = gfs2_meta_read(ip->i_gl, dblock, 0, 0, &bh);
if (error)
goto fail;
}
@@ -757,7 +757,7 @@ static int get_leaf(struct gfs2_inode *dip, u64 leaf_no,
{
int error;
- error = gfs2_meta_read(dip->i_gl, leaf_no, 0, bhp);
+ error = gfs2_meta_read(dip->i_gl, leaf_no, 0, 0, bhp);
if (!error && gfs2_metatype_check(GFS2_SB(&dip->i_inode), *bhp, GFS2_METATYPE_LF)) {
/* pr_info("block num=%llu\n", leaf_no); */
error = -EIO;
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index 6492d000d366..47a430206801 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -243,13 +243,16 @@ static void gfs2_submit_bhs(blk_opf_t opf, struct buffer_head *bhs[], int num)
* gfs2_meta_read_async - Read a block from disk
* @gl: The glock covering the block
* @blkno: The block number
+ * @fgp_flags: FGP_NOWAIT if sleeping is prohibited
* @rahead: Do read-ahead
* @bhp: the place where the buffer is returned (NULL on failure)
*
+ * Returns -EAGAIN if the FGP_NOWAIT flag is set and the function would sleep.
+ *
* Returns: errno
*/
-int gfs2_meta_read_async(struct gfs2_glock *gl, u64 blkno,
+int gfs2_meta_read_async(struct gfs2_glock *gl, u64 blkno, fgf_t fgp_flags,
int rahead, struct buffer_head **bhp)
{
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
@@ -262,7 +265,29 @@ int gfs2_meta_read_async(struct gfs2_glock *gl, u64 blkno,
return -EIO;
}
- *bhp = bh = gfs2_getbuf(gl, blkno, FGP_CREAT);
+ fgp_flags |= FGP_CREAT;
+ *bhp = bh = gfs2_getbuf(gl, blkno, fgp_flags);
+ if (IS_ERR(bh)) {
+ *bhp = NULL;
+ return PTR_ERR(bh);
+ }
+
+ if (fgp_flags & FGP_NOWAIT) {
+ bool uptodate = false;
+
+ /* skips readahead entirely. */
+
+ if (trylock_buffer(bh)) {
+ uptodate = buffer_uptodate(bh);
+ unlock_buffer(bh);
+ }
+ if (!uptodate) {
+ brelse(bh);
+ *bhp = NULL;
+ return -EAGAIN;
+ }
+ return 0;
+ }
lock_buffer(bh);
if (buffer_uptodate(bh)) {
@@ -274,7 +299,9 @@ int gfs2_meta_read_async(struct gfs2_glock *gl, u64 blkno,
}
if (rahead) {
- bh = gfs2_getbuf(gl, blkno + 1, FGP_CREAT);
+ bh = gfs2_getbuf(gl, blkno + 1, fgp_flags);
+ if (IS_ERR(bh))
+ goto out;
lock_buffer(bh);
if (buffer_uptodate(bh)) {
@@ -286,6 +313,7 @@ int gfs2_meta_read_async(struct gfs2_glock *gl, u64 blkno,
}
}
+out:
gfs2_submit_bhs(REQ_OP_READ | REQ_META | REQ_PRIO, bhs, num);
return 0;
}
@@ -315,13 +343,13 @@ int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh)
return 0;
}
-int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno,
+int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, fgf_t fgp_flags,
int rahead, struct buffer_head **bhp)
{
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
int ret;
- ret = gfs2_meta_read_async(gl, blkno, rahead, bhp);
+ ret = gfs2_meta_read_async(gl, blkno, fgp_flags, rahead, bhp);
if (ret)
return ret;
@@ -489,7 +517,7 @@ int gfs2_meta_buffer(struct gfs2_inode *ip, u32 mtype, u64 num,
if (num == ip->i_no_addr)
rahead = ip->i_rahead;
- ret = gfs2_meta_read(gl, num, rahead, &bh);
+ ret = gfs2_meta_read(gl, num, 0, rahead, &bh);
if (ret == 0 && gfs2_metatype_check(sdp, bh, mtype)) {
brelse(bh);
ret = -EIO;
diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h
index f04c91eadfb4..6ca37e9f1c95 100644
--- a/fs/gfs2/meta_io.h
+++ b/fs/gfs2/meta_io.h
@@ -51,10 +51,10 @@ static inline struct gfs2_sbd *gfs2_mapping2sbd(struct address_space *mapping)
}
struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno);
-int gfs2_meta_read_async(struct gfs2_glock *gl, u64 blkno,
+int gfs2_meta_read_async(struct gfs2_glock *gl, u64 blkno, fgf_t fgp_flags,
int rahead, struct buffer_head **bhp);
int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh);
-int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno,
+int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, fgf_t fgp_flags,
int rahead, struct buffer_head **bhp);
struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno,
fgf_t fgp_flags);
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index a68af8bdf7de..8b47306816ab 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -424,7 +424,7 @@ static int bh_get(struct gfs2_quota_data *qd)
goto fail;
error = gfs2_meta_read(ip->i_gl, iomap.addr >> inode->i_blkbits,
- 0, &bh);
+ 0, 0, &bh);
if (error)
goto fail;
error = -EIO;
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index fcef82c767e3..9fa4fc7f4bcf 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -1210,7 +1210,7 @@ int gfs2_rgrp_go_instantiate(struct gfs2_glock *gl)
for (x = 0; x < length; x++) {
bi = rgd->rd_bits + x;
- error = gfs2_meta_read_async(gl, rgd->rd_addr + x, 0,
+ error = gfs2_meta_read_async(gl, rgd->rd_addr + x, 0, 0,
&bi->bi_bh);
if (error)
goto fail;
diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c
index 759347ec67af..745c7cf78519 100644
--- a/fs/gfs2/xattr.c
+++ b/fs/gfs2/xattr.c
@@ -128,7 +128,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
__be64 *eablk, *end;
int error;
- error = gfs2_meta_read(ip->i_gl, ip->i_eattr, 0, &bh);
+ error = gfs2_meta_read(ip->i_gl, ip->i_eattr, 0, 0, &bh);
if (error)
return error;
@@ -152,7 +152,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
break;
bn = be64_to_cpu(*eablk);
- error = gfs2_meta_read(ip->i_gl, bn, 0, &eabh);
+ error = gfs2_meta_read(ip->i_gl, bn, 0, 0, &eabh);
if (error)
break;
error = ea_foreach_i(ip, eabh, ea_call, data);
@@ -469,7 +469,7 @@ static int gfs2_iter_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
for (x = 0; x < nptrs; x++) {
error = gfs2_meta_read_async(ip->i_gl, be64_to_cpu(*dataptrs),
- 0, bh + x);
+ 0, 0, bh + x);
if (error) {
while (x--)
brelse(bh[x]);
@@ -976,7 +976,7 @@ static int ea_set_block(struct gfs2_inode *ip, struct gfs2_ea_request *er,
if (ip->i_diskflags & GFS2_DIF_EA_INDIRECT) {
__be64 *end;
- error = gfs2_meta_read(ip->i_gl, ip->i_eattr, 0, &indbh);
+ error = gfs2_meta_read(ip->i_gl, ip->i_eattr, 0, 0, &indbh);
if (error)
return error;
@@ -1278,7 +1278,7 @@ static int ea_dealloc_indirect(struct gfs2_inode *ip)
memset(&rlist, 0, sizeof(struct gfs2_rgrp_list));
- error = gfs2_meta_read(ip->i_gl, ip->i_eattr, 0, &indbh);
+ error = gfs2_meta_read(ip->i_gl, ip->i_eattr, 0, 0, &indbh);
if (error)
return error;
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 5/9] gfs2: Pass FGP flags to gfs2_meta_{,inode_}buffer
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
` (3 preceding siblings ...)
2024-01-19 21:20 ` [PATCH 4/9] gfs2: Add FGP_NOWAIT support to gfs2_meta_read_async Andreas Gruenbacher
@ 2024-01-19 21:20 ` Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 6/9] gfs2: Pass FGP flags to gfs2_dirent_search Andreas Gruenbacher
` (6 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-01-19 21:20 UTC (permalink / raw)
To: gfs2; +Cc: Al Viro, Andreas Gruenbacher
Pass a fgp_flags argument to gfs2_meta_buffer() and
gfs2_meta_inode_buffer(). If the FGP_NOWAIT flag is set and one of
these functions would sleep, -EAGAIN is returned instead.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/aops.c | 4 ++--
fs/gfs2/bmap.c | 19 ++++++++++---------
fs/gfs2/dir.c | 20 ++++++++++----------
fs/gfs2/file.c | 4 ++--
fs/gfs2/glops.c | 2 +-
fs/gfs2/inode.c | 4 ++--
fs/gfs2/meta_io.c | 7 +++++--
fs/gfs2/meta_io.h | 7 ++++---
fs/gfs2/ops_fstype.c | 2 +-
fs/gfs2/recovery.c | 2 +-
fs/gfs2/rgrp.c | 2 +-
fs/gfs2/super.c | 6 +++---
fs/gfs2/xattr.c | 4 ++--
13 files changed, 44 insertions(+), 39 deletions(-)
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 974aca9c8ea8..31da7f6926c9 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -424,7 +424,7 @@ static int stuffed_read_folio(struct gfs2_inode *ip, struct folio *folio)
if (unlikely(folio->index)) {
dsize = 0;
} else {
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error)
goto out;
from = dibh->b_data + sizeof(struct gfs2_dinode);
@@ -548,7 +548,7 @@ void adjust_fs_space(struct inode *inode)
/* Total up the file system space, according to the latest rindex. */
fs_total = gfs2_ri_total(sdp);
- if (gfs2_meta_inode_buffer(m_ip, &m_bh) != 0)
+ if (gfs2_meta_inode_buffer(m_ip, 0, &m_bh) != 0)
goto out;
spin_lock(&sdp->sd_statfs_spin);
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 92945e5b7643..ffe19d3012f5 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -95,7 +95,7 @@ static int __gfs2_unstuff_inode(struct gfs2_inode *ip, struct folio *folio)
int isdir = gfs2_is_dir(ip);
int error;
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error)
return error;
@@ -331,7 +331,8 @@ static int __fillup_metapath(struct gfs2_inode *ip, struct metapath *mp,
if (!dblock)
break;
- ret = gfs2_meta_buffer(ip, GFS2_METATYPE_IN, dblock, &mp->mp_bh[x + 1]);
+ ret = gfs2_meta_buffer(ip, GFS2_METATYPE_IN, dblock, 0,
+ &mp->mp_bh[x + 1]);
if (ret)
return ret;
}
@@ -858,7 +859,7 @@ static int __gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length,
down_read(&ip->i_rw_mutex);
- ret = gfs2_meta_inode_buffer(ip, &dibh);
+ ret = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (ret)
goto unlock;
mp->mp_bh[0] = dibh;
@@ -1377,7 +1378,7 @@ static int trunc_start(struct inode *inode, u64 newsize)
if (error)
return error;
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error)
goto out;
@@ -1580,7 +1581,7 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
if (current->journal_info) {
struct buffer_head *dibh;
- ret = gfs2_meta_inode_buffer(ip, &dibh);
+ ret = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (ret)
goto out;
@@ -1785,7 +1786,7 @@ static int punch_hole(struct gfs2_inode *ip, u64 offset, u64 length)
}
start_aligned = mp_h;
- ret = gfs2_meta_inode_buffer(ip, &dibh);
+ ret = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (ret)
return ret;
@@ -1985,7 +1986,7 @@ static int trunc_end(struct gfs2_inode *ip)
down_write(&ip->i_rw_mutex);
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error)
goto out;
@@ -2091,7 +2092,7 @@ static int do_grow(struct inode *inode, u64 size)
goto do_end_trans;
}
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error)
goto do_end_trans;
@@ -2345,7 +2346,7 @@ static int stuffed_zero_range(struct inode *inode, loff_t offset, loff_t length)
if (offset + length > inode->i_size)
length = inode->i_size - offset;
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error)
return error;
gfs2_trans_add_meta(ip->i_gl, dibh);
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index a1f47696a2ec..8719c8316e28 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -122,7 +122,7 @@ static int gfs2_dir_write_stuffed(struct gfs2_inode *ip, const char *buf,
struct buffer_head *dibh;
int error;
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error)
return error;
@@ -221,7 +221,7 @@ static int gfs2_dir_write_data(struct gfs2_inode *ip, const char *buf,
}
out:
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error)
return error;
@@ -246,7 +246,7 @@ static int gfs2_dir_read_stuffed(struct gfs2_inode *ip, __be64 *buf,
struct buffer_head *dibh;
int error;
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (!error) {
memcpy(buf, dibh->b_data + sizeof(struct gfs2_dinode), size);
brelse(dibh);
@@ -844,7 +844,7 @@ static struct gfs2_dirent *gfs2_dirent_search(struct inode *inode,
}
- error = gfs2_meta_inode_buffer(ip, &bh);
+ error = gfs2_meta_inode_buffer(ip, 0, &bh);
if (error)
return ERR_PTR(error);
dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, scan, name, NULL);
@@ -915,7 +915,7 @@ static int dir_make_exhash(struct inode *inode)
u64 bn;
int error;
- error = gfs2_meta_inode_buffer(dip, &dibh);
+ error = gfs2_meta_inode_buffer(dip, 0, &dibh);
if (error)
return error;
@@ -1115,7 +1115,7 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name)
oleaf->lf_depth = nleaf->lf_depth;
- error = gfs2_meta_inode_buffer(dip, &dibh);
+ error = gfs2_meta_inode_buffer(dip, 0, &dibh);
if (!gfs2_assert_withdraw(GFS2_SB(&dip->i_inode), !error)) {
gfs2_trans_add_meta(dip->i_gl, dibh);
gfs2_add_inode_blocks(&dip->i_inode, 1);
@@ -1169,7 +1169,7 @@ static int dir_double_exhash(struct gfs2_inode *dip)
return -ENOMEM;
h = hc2;
- error = gfs2_meta_inode_buffer(dip, &dibh);
+ error = gfs2_meta_inode_buffer(dip, 0, &dibh);
if (error)
goto out_kfree;
@@ -1586,7 +1586,7 @@ int gfs2_dir_read(struct inode *inode, struct dir_context *ctx,
return -EIO;
}
- error = gfs2_meta_inode_buffer(dip, &dibh);
+ error = gfs2_meta_inode_buffer(dip, 0, &dibh);
if (error)
return error;
@@ -1758,7 +1758,7 @@ static int dir_new_leaf(struct inode *inode, const struct qstr *name)
brelse(bh);
brelse(obh);
- error = gfs2_meta_inode_buffer(ip, &bh);
+ error = gfs2_meta_inode_buffer(ip, 0, &bh);
if (error)
return error;
gfs2_trans_add_meta(ip->i_gl, bh);
@@ -2063,7 +2063,7 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
goto out_end_trans;
}
- error = gfs2_meta_inode_buffer(dip, &dibh);
+ error = gfs2_meta_inode_buffer(dip, 0, &dibh);
if (error)
goto out_end_trans;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 992ca4effb50..ac4ed6abfc6c 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -257,7 +257,7 @@ static int do_gfs2_set_flags(struct inode *inode, u32 reqflags, u32 mask)
error = gfs2_trans_begin(sdp, RES_DINODE, 0);
if (error)
goto out;
- error = gfs2_meta_inode_buffer(ip, &bh);
+ error = gfs2_meta_inode_buffer(ip, 0, &bh);
if (error)
goto out_trans_end;
inode_set_ctime_current(inode);
@@ -1179,7 +1179,7 @@ static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len,
struct buffer_head *dibh;
int error;
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (unlikely(error))
return error;
diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index 45653cbc8a87..cc38aa81d246 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -483,7 +483,7 @@ int gfs2_inode_refresh(struct gfs2_inode *ip)
struct buffer_head *dibh;
int error;
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error)
return error;
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 6bfc9383b7b8..a417650e378f 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -1027,7 +1027,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
goto out_ipres;
}
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error)
goto out_end_trans;
@@ -1833,7 +1833,7 @@ static const char *gfs2_get_link(struct dentry *dentry,
goto out;
}
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error) {
buf = ERR_PTR(error);
goto out;
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index 47a430206801..c8750b337496 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -500,13 +500,16 @@ void gfs2_journal_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen)
* @ip: The GFS2 inode
* @mtype: The block type (GFS2_METATYPE_*)
* @num: The block number (device relative) of the buffer
+ * @fgp_flags: FGP_NOWAIT if sleeping is prohibited
* @bhp: the buffer is returned here
*
+ * Returns -EAGAIN if the FGP_NOWAIT flag is set and the function would sleep.
+ *
* Returns: errno
*/
int gfs2_meta_buffer(struct gfs2_inode *ip, u32 mtype, u64 num,
- struct buffer_head **bhp)
+ fgf_t fgp_flags, struct buffer_head **bhp)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_glock *gl = ip->i_gl;
@@ -517,7 +520,7 @@ int gfs2_meta_buffer(struct gfs2_inode *ip, u32 mtype, u64 num,
if (num == ip->i_no_addr)
rahead = ip->i_rahead;
- ret = gfs2_meta_read(gl, num, 0, rahead, &bh);
+ ret = gfs2_meta_read(gl, num, fgp_flags, rahead, &bh);
if (ret == 0 && gfs2_metatype_check(sdp, bh, mtype)) {
brelse(bh);
ret = -EIO;
diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h
index 6ca37e9f1c95..7b5122078113 100644
--- a/fs/gfs2/meta_io.h
+++ b/fs/gfs2/meta_io.h
@@ -66,12 +66,13 @@ enum {
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);
+ fgf_t fgp_flags, struct buffer_head **bhp);
-static inline int gfs2_meta_inode_buffer(struct gfs2_inode *ip,
+static inline int gfs2_meta_inode_buffer(struct gfs2_inode *ip, fgf_t fgp_flags,
struct buffer_head **bhp)
{
- return gfs2_meta_buffer(ip, GFS2_METATYPE_DI, ip->i_no_addr, bhp);
+ return gfs2_meta_buffer(ip, GFS2_METATYPE_DI, ip->i_no_addr, fgp_flags,
+ bhp);
}
struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen);
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 1281e60be639..1a01adb48f5b 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -698,7 +698,7 @@ static int init_statfs(struct gfs2_sbd *sdp)
goto free_local;
}
/* read in the local statfs buffer - other nodes don't change it. */
- error = gfs2_meta_inode_buffer(ip, &sdp->sd_sc_bh);
+ error = gfs2_meta_inode_buffer(ip, 0, &sdp->sd_sc_bh);
if (error) {
fs_err(sdp, "Cannot read in local statfs: %d\n", error);
goto unlock_sd_gh;
diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c
index f4fe7039f725..4db3ca9c3b02 100644
--- a/fs/gfs2/recovery.c
+++ b/fs/gfs2/recovery.c
@@ -317,7 +317,7 @@ static int update_statfs_inode(struct gfs2_jdesc *jd,
BUG_ON(!inode);
ip = GFS2_I(inode);
- error = gfs2_meta_inode_buffer(ip, &bh);
+ error = gfs2_meta_inode_buffer(ip, 0, &bh);
if (error)
goto out;
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index 9fa4fc7f4bcf..c9aec3dba80c 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -2452,7 +2452,7 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
rbm.rgd->rd_last_alloc = block - rbm.rgd->rd_data0;
if (!dinode) {
ip->i_goal = block + *nblocks - 1;
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (error == 0) {
struct gfs2_dinode *di =
(struct gfs2_dinode *)dibh->b_data;
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index e5f79466340d..7bfdd5924a54 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -192,7 +192,7 @@ int gfs2_statfs_init(struct gfs2_sbd *sdp)
if (error)
return error;
- error = gfs2_meta_inode_buffer(m_ip, &m_bh);
+ error = gfs2_meta_inode_buffer(m_ip, 0, &m_bh);
if (error)
goto out;
@@ -282,7 +282,7 @@ int gfs2_statfs_sync(struct super_block *sb, int type)
if (error)
goto out;
- error = gfs2_meta_inode_buffer(m_ip, &m_bh);
+ error = gfs2_meta_inode_buffer(m_ip, 0, &m_bh);
if (error)
goto out_unlock;
@@ -521,7 +521,7 @@ static void gfs2_dirty_inode(struct inode *inode, int flags)
need_endtrans = 1;
}
- ret = gfs2_meta_inode_buffer(ip, &bh);
+ ret = gfs2_meta_inode_buffer(ip, 0, &bh);
if (ret == 0) {
gfs2_trans_add_meta(ip->i_gl, bh);
gfs2_dinode_out(ip, bh->b_data);
diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c
index 745c7cf78519..e2ff0fc8e445 100644
--- a/fs/gfs2/xattr.c
+++ b/fs/gfs2/xattr.c
@@ -1360,7 +1360,7 @@ static int ea_dealloc_indirect(struct gfs2_inode *ip)
ip->i_diskflags &= ~GFS2_DIF_EA_INDIRECT;
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (!error) {
gfs2_trans_add_meta(ip->i_gl, dibh);
gfs2_dinode_out(ip, dibh->b_data);
@@ -1412,7 +1412,7 @@ static int ea_dealloc_block(struct gfs2_inode *ip)
gfs2_add_inode_blocks(&ip->i_inode, -1);
if (likely(!test_bit(GIF_ALLOC_FAILED, &ip->i_flags))) {
- error = gfs2_meta_inode_buffer(ip, &dibh);
+ error = gfs2_meta_inode_buffer(ip, 0, &dibh);
if (!error) {
gfs2_trans_add_meta(ip->i_gl, dibh);
gfs2_dinode_out(ip, dibh->b_data);
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 6/9] gfs2: Pass FGP flags to gfs2_dirent_search
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
` (4 preceding siblings ...)
2024-01-19 21:20 ` [PATCH 5/9] gfs2: Pass FGP flags to gfs2_meta_{,inode_}buffer Andreas Gruenbacher
@ 2024-01-19 21:20 ` Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 7/9] gfs2: Pass FGP flags to gfs2_dir_check Andreas Gruenbacher
` (5 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-01-19 21:20 UTC (permalink / raw)
To: gfs2; +Cc: Al Viro, Andreas Gruenbacher
Add an fgp_flags argument to gfs2_dirent_search(). When the FGP_NOWAIT
flag is set and gfs2_dirent_search() would sleep, -EAGAIN is returned
instead.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/dir.c | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 8719c8316e28..81d1ea61f44f 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -805,6 +805,7 @@ static int get_first_leaf(struct gfs2_inode *dip, u32 index,
static struct gfs2_dirent *gfs2_dirent_search(struct inode *inode,
const struct qstr *name,
gfs2_dscan_t scan,
+ fgf_t fgp_flags,
struct buffer_head **pbh)
{
struct buffer_head *bh;
@@ -817,6 +818,11 @@ static struct gfs2_dirent *gfs2_dirent_search(struct inode *inode,
unsigned int hsize = BIT(ip->i_depth);
unsigned int index;
u64 ln;
+
+ /* no lockless lookup inside ExHash directories */
+ if (fgp_flags & FGP_NOWAIT)
+ return ERR_PTR(-EAGAIN);
+
if (hsize * sizeof(u64) != i_size_read(inode)) {
gfs2_consist_inode(ip);
return ERR_PTR(-EIO);
@@ -843,8 +849,7 @@ static struct gfs2_dirent *gfs2_dirent_search(struct inode *inode,
return error ? ERR_PTR(error) : NULL;
}
-
- error = gfs2_meta_inode_buffer(ip, 0, &bh);
+ error = gfs2_meta_inode_buffer(ip, fgp_flags, &bh);
if (error)
return ERR_PTR(error);
dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, scan, name, NULL);
@@ -1647,7 +1652,7 @@ struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name,
u64 addr, formal_ino;
u16 dtype;
- dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, &bh);
+ dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, 0, &bh);
if (dent) {
struct inode *inode;
u16 rahead;
@@ -1677,7 +1682,7 @@ int gfs2_dir_check(struct inode *dir, const struct qstr *name,
struct gfs2_dirent *dent;
int ret = -ENOENT;
- dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, &bh);
+ dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, 0, &bh);
if (dent) {
if (IS_ERR(dent))
return PTR_ERR(dent);
@@ -1805,7 +1810,8 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name,
while(1) {
if (da->bh == NULL) {
dent = gfs2_dirent_search(inode, name,
- gfs2_dirent_find_space, &bh);
+ gfs2_dirent_find_space, 0,
+ &bh);
}
if (dent) {
if (IS_ERR(dent))
@@ -1880,7 +1886,8 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry)
/* Returns _either_ the entry (if its first in block) or the
previous entry otherwise */
- dent = gfs2_dirent_search(&dip->i_inode, name, gfs2_dirent_prev, &bh);
+ dent = gfs2_dirent_search(&dip->i_inode, name, gfs2_dirent_prev, 0,
+ &bh);
if (!dent) {
gfs2_consist_inode(dip);
return -EIO;
@@ -1939,7 +1946,8 @@ int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename,
struct buffer_head *bh;
struct gfs2_dirent *dent;
- dent = gfs2_dirent_search(&dip->i_inode, filename, gfs2_dirent_find, &bh);
+ dent = gfs2_dirent_search(&dip->i_inode, filename, gfs2_dirent_find, 0,
+ &bh);
if (!dent) {
gfs2_consist_inode(dip);
return -EIO;
@@ -2166,7 +2174,7 @@ int gfs2_diradd_alloc_required(struct inode *inode, const struct qstr *name,
da->bh = NULL;
da->dent = NULL;
- dent = gfs2_dirent_search(inode, name, gfs2_dirent_find_space, &bh);
+ dent = gfs2_dirent_search(inode, name, gfs2_dirent_find_space, 0, &bh);
if (!dent) {
da->nr_blocks = sdp->sd_max_dirres;
if (!(ip->i_diskflags & GFS2_DIF_EXHASH) &&
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 7/9] gfs2: Pass FGP flags to gfs2_dir_check
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
` (5 preceding siblings ...)
2024-01-19 21:20 ` [PATCH 6/9] gfs2: Pass FGP flags to gfs2_dirent_search Andreas Gruenbacher
@ 2024-01-19 21:20 ` Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 8/9] gfs2: Minor gfs2_drevalidate cleanup Andreas Gruenbacher
` (4 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-01-19 21:20 UTC (permalink / raw)
To: gfs2; +Cc: Al Viro, Andreas Gruenbacher
Pass a fgp_flags argument to gfs2_dir_check(). If the FGP_NOWAIT flag
is set and gfs2_dir_check() would sleep, -EAGAIN is returned instead.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/dentry.c | 2 +-
fs/gfs2/dir.c | 4 ++--
fs/gfs2/dir.h | 2 +-
fs/gfs2/inode.c | 6 +++---
fs/gfs2/ops_fstype.c | 2 +-
5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c
index c6483fb98624..f179a6d94cbe 100644
--- a/fs/gfs2/dentry.c
+++ b/fs/gfs2/dentry.c
@@ -72,7 +72,7 @@ static int gfs2_drevalidate(struct dentry *dentry, unsigned int flags)
goto out;
}
- error = gfs2_dir_check(dinode, &dentry->d_name, ip);
+ error = gfs2_dir_check(dinode, &dentry->d_name, ip, 0);
valid = inode ? !error : (error == -ENOENT);
if (!had_lock)
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 81d1ea61f44f..d3a9c9094db0 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -1676,13 +1676,13 @@ struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name,
}
int gfs2_dir_check(struct inode *dir, const struct qstr *name,
- const struct gfs2_inode *ip)
+ const struct gfs2_inode *ip, fgf_t fgp_flags)
{
struct buffer_head *bh;
struct gfs2_dirent *dent;
int ret = -ENOENT;
- dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, 0, &bh);
+ dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, fgp_flags, &bh);
if (dent) {
if (IS_ERR(dent))
return PTR_ERR(dent);
diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h
index 25a857c78b53..cfec869a11c8 100644
--- a/fs/gfs2/dir.h
+++ b/fs/gfs2/dir.h
@@ -27,7 +27,7 @@ struct inode *gfs2_dir_search(struct inode *dir,
const struct qstr *filename,
bool fail_on_exist);
int gfs2_dir_check(struct inode *dir, const struct qstr *filename,
- const struct gfs2_inode *ip);
+ const struct gfs2_inode *ip, fgf_t fgp_flags);
int gfs2_dir_add(struct inode *inode, const struct qstr *filename,
const struct gfs2_inode *ip, struct gfs2_diradd *da);
static inline void gfs2_dir_no_add(struct gfs2_diradd *da)
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index a417650e378f..948f5c203a38 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -980,7 +980,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
if (error)
goto out_gunlock;
- error = gfs2_dir_check(dir, &dentry->d_name, NULL);
+ error = gfs2_dir_check(dir, &dentry->d_name, NULL, 0);
switch (error) {
case -ENOENT:
break;
@@ -1096,7 +1096,7 @@ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
if (error)
return error;
- return gfs2_dir_check(&dip->i_inode, name, ip);
+ return gfs2_dir_check(&dip->i_inode, name, ip, 0);
}
/**
@@ -1522,7 +1522,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
if (error)
goto out_gunlock;
- error = gfs2_dir_check(ndir, &ndentry->d_name, NULL);
+ error = gfs2_dir_check(ndir, &ndentry->d_name, NULL, 0);
switch (error) {
case -ENOENT:
error = 0;
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 1a01adb48f5b..f6c80ff618cc 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -584,7 +584,7 @@ static int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh)
name.len = sprintf(buf, "journal%u", sdp->sd_journals);
name.hash = gfs2_disk_hash(name.name, name.len);
- error = gfs2_dir_check(sdp->sd_jindex, &name, NULL);
+ error = gfs2_dir_check(sdp->sd_jindex, &name, NULL, 0);
if (error == -ENOENT) {
error = 0;
break;
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 8/9] gfs2: Minor gfs2_drevalidate cleanup
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
` (6 preceding siblings ...)
2024-01-19 21:20 ` [PATCH 7/9] gfs2: Pass FGP flags to gfs2_dir_check Andreas Gruenbacher
@ 2024-01-19 21:20 ` Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 9/9] gfs2: Fix LOOKUP_RCU support in gfs2_drevalidate Andreas Gruenbacher
` (3 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-01-19 21:20 UTC (permalink / raw)
To: gfs2; +Cc: Al Viro, Andreas Gruenbacher
Get rid of the had_lock and valid variables in gfs2_drevalidate().
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/dentry.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c
index f179a6d94cbe..99239d80982b 100644
--- a/fs/gfs2/dentry.c
+++ b/fs/gfs2/dentry.c
@@ -38,8 +38,9 @@ static int gfs2_drevalidate(struct dentry *dentry, unsigned int flags)
struct inode *dinode, *inode;
struct gfs2_holder d_gh;
struct gfs2_inode *ip = NULL;
- int error, valid = 0;
- int had_lock = 0;
+ int error;
+
+ gfs2_holder_mark_uninitialized(&d_gh);
if (flags & LOOKUP_RCU) {
dinode = d_inode_rcu(READ_ONCE(dentry->d_parent));
@@ -54,18 +55,19 @@ static int gfs2_drevalidate(struct dentry *dentry, unsigned int flags)
inode = d_inode(dentry);
if (inode) {
- if (is_bad_inode(inode))
+ if (is_bad_inode(inode)) {
+ error = 0;
goto out;
+ }
ip = GFS2_I(inode);
}
if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) {
- valid = 1;
+ error = 1;
goto out;
}
- had_lock = (gfs2_glock_is_locked_by_me(dip->i_gl) != NULL);
- if (!had_lock) {
+ if (!gfs2_glock_is_locked_by_me(dip->i_gl)) {
error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED,
flags & LOOKUP_RCU ? GL_NOBLOCK : 0, &d_gh);
if (error)
@@ -73,13 +75,13 @@ static int gfs2_drevalidate(struct dentry *dentry, unsigned int flags)
}
error = gfs2_dir_check(dinode, &dentry->d_name, ip, 0);
- valid = inode ? !error : (error == -ENOENT);
+ error = inode ? !error : (error == -ENOENT);
- if (!had_lock)
+ if (gfs2_holder_initialized(&d_gh))
gfs2_glock_dq_uninit(&d_gh);
out:
dput(parent);
- return valid;
+ return error;
}
static int gfs2_dhash(const struct dentry *dentry, struct qstr *str)
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 9/9] gfs2: Fix LOOKUP_RCU support in gfs2_drevalidate
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
` (7 preceding siblings ...)
2024-01-19 21:20 ` [PATCH 8/9] gfs2: Minor gfs2_drevalidate cleanup Andreas Gruenbacher
@ 2024-01-19 21:20 ` Andreas Gruenbacher
2024-01-20 1:36 ` [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Al Viro
` (2 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-01-19 21:20 UTC (permalink / raw)
To: gfs2; +Cc: Al Viro, Andreas Gruenbacher
Fix non-sleeping lookups in gfs2_drevalidate() by passing the FGP_NOWAIT
flag down to gfs2_dir_check(). This will cause gfs2_dir_check() to
return -EAGAIN when it would otherwise sleep.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/dentry.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c
index 99239d80982b..b14956cdab0e 100644
--- a/fs/gfs2/dentry.c
+++ b/fs/gfs2/dentry.c
@@ -74,12 +74,20 @@ static int gfs2_drevalidate(struct dentry *dentry, unsigned int flags)
goto out;
}
- error = gfs2_dir_check(dinode, &dentry->d_name, ip, 0);
+ if (flags & LOOKUP_RCU) {
+ error = gfs2_dir_check(dinode, &dentry->d_name, ip, FGP_NOWAIT);
+ if (error == -EAGAIN) {
+ error = -ECHILD;
+ goto out;
+ }
+ } else {
+ error = gfs2_dir_check(dinode, &dentry->d_name, ip, 0);
+ }
error = inode ? !error : (error == -ENOENT);
+out:
if (gfs2_holder_initialized(&d_gh))
gfs2_glock_dq_uninit(&d_gh);
-out:
dput(parent);
return error;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups"
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
` (8 preceding siblings ...)
2024-01-19 21:20 ` [PATCH 9/9] gfs2: Fix LOOKUP_RCU support in gfs2_drevalidate Andreas Gruenbacher
@ 2024-01-20 1:36 ` Al Viro
2024-01-20 1:38 ` Al Viro
2024-01-22 12:52 ` Andrew Price
2024-02-02 4:23 ` Al Viro
11 siblings, 1 reply; 19+ messages in thread
From: Al Viro @ 2024-01-20 1:36 UTC (permalink / raw)
To: Andreas Gruenbacher; +Cc: gfs2
On Fri, Jan 19, 2024 at 10:20:47PM +0100, Andreas Gruenbacher wrote:
> Hello,
>
> Al Viro has reported issues with commit dd00aaeb3432 ("gfs2: Use
> GL_NOBLOCK flag for non-blocking lookups"):
>
> * First, parent can now be NULL and dereferencing it in
> gfs2_dir_check(d_inode(parent), &dentry->d_name, ip) isn't going to
> work;
Nit: parent can't be NULL; what can be NULL in RCU mode is d_inode(parent) when
sampled inside ->d_revalidate(). IOW, it's not d_inode(parent) evaluation
that breaks, but passing the resulting NULL inode pointer to gfs2_dir_check().
> * Second, gfs2_dir_check() can still sleep, which breaks LOOKUP_RCU
> mode.
>
> These patches should fix both of these problems. We'll either need
> these fixes added, or commit dd00aaeb3432 reverted for now.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups"
2024-01-20 1:36 ` [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Al Viro
@ 2024-01-20 1:38 ` Al Viro
0 siblings, 0 replies; 19+ messages in thread
From: Al Viro @ 2024-01-20 1:38 UTC (permalink / raw)
To: Andreas Gruenbacher; +Cc: gfs2
On Sat, Jan 20, 2024 at 01:36:39AM +0000, Al Viro wrote:
> On Fri, Jan 19, 2024 at 10:20:47PM +0100, Andreas Gruenbacher wrote:
> > Hello,
> >
> > Al Viro has reported issues with commit dd00aaeb3432 ("gfs2: Use
> > GL_NOBLOCK flag for non-blocking lookups"):
> >
> > * First, parent can now be NULL and dereferencing it in
> > gfs2_dir_check(d_inode(parent), &dentry->d_name, ip) isn't going to
> > work;
>
> Nit: parent can't be NULL; what can be NULL in RCU mode is d_inode(parent) when
> sampled inside ->d_revalidate(). IOW, it's not d_inode(parent) evaluation
> that breaks, but passing the resulting NULL inode pointer to gfs2_dir_check().
Nevermind, the way it's written parent will be NULL in RCU case. Sorry about
the noise.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups"
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
` (9 preceding siblings ...)
2024-01-20 1:36 ` [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Al Viro
@ 2024-01-22 12:52 ` Andrew Price
2024-02-02 4:23 ` Al Viro
11 siblings, 0 replies; 19+ messages in thread
From: Andrew Price @ 2024-01-22 12:52 UTC (permalink / raw)
To: Andreas Gruenbacher; +Cc: Al Viro, gfs2
On 19/01/2024 21:20, Andreas Gruenbacher wrote:
> Hello,
>
> Al Viro has reported issues with commit dd00aaeb3432 ("gfs2: Use
> GL_NOBLOCK flag for non-blocking lookups"):
>
> * First, parent can now be NULL and dereferencing it in
> gfs2_dir_check(d_inode(parent), &dentry->d_name, ip) isn't going to
> work;
>
> * Second, gfs2_dir_check() can still sleep, which breaks LOOKUP_RCU
> mode.
>
> These patches should fix both of these problems. We'll either need
> these fixes added, or commit dd00aaeb3432 reverted for now.
No issues spotted. I like the gfs2_meta_read() improvement. If it helps:
Reviewed-by: Andrew Price <anprice@redhat.com>
Andy
> Thanks,
> Andreas
>
> Andreas Gruenbacher (9):
> gfs2: Fix gfs2_drevalidate NULL pointer dereference
> gfs2: Pass FGP flags to gfs2_getbuf
> gfs2: Split gfs2_meta_read_async off from gfs2_meta_read
> gfs2: Add FGP_NOWAIT support to gfs2_meta_read_async
> gfs2: Pass FGP flags to gfs2_meta_{,inode_}buffer
> gfs2: Pass FGP flags to gfs2_dirent_search
> gfs2: Pass FGP flags to gfs2_dir_check
> gfs2: Minor gfs2_drevalidate cleanup
> gfs2: Fix LOOKUP_RCU support in gfs2_drevalidate
>
> fs/gfs2/aops.c | 4 +-
> fs/gfs2/bmap.c | 21 ++++----
> fs/gfs2/dentry.c | 32 ++++++++-----
> fs/gfs2/dir.c | 52 +++++++++++---------
> fs/gfs2/dir.h | 2 +-
> fs/gfs2/file.c | 4 +-
> fs/gfs2/glops.c | 2 +-
> fs/gfs2/incore.h | 1 -
> fs/gfs2/inode.c | 10 ++--
> fs/gfs2/meta_io.c | 112 ++++++++++++++++++++++++++++---------------
> fs/gfs2/meta_io.h | 15 +++---
> fs/gfs2/ops_fstype.c | 4 +-
> fs/gfs2/quota.c | 2 +-
> fs/gfs2/recovery.c | 2 +-
> fs/gfs2/rgrp.c | 5 +-
> fs/gfs2/super.c | 6 +--
> fs/gfs2/xattr.c | 17 ++++---
> 17 files changed, 173 insertions(+), 118 deletions(-)
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups"
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
` (10 preceding siblings ...)
2024-01-22 12:52 ` Andrew Price
@ 2024-02-02 4:23 ` Al Viro
2024-02-02 4:34 ` Al Viro
2024-02-02 4:59 ` Al Viro
11 siblings, 2 replies; 19+ messages in thread
From: Al Viro @ 2024-02-02 4:23 UTC (permalink / raw)
To: Andreas Gruenbacher; +Cc: gfs2
On Fri, Jan 19, 2024 at 10:20:47PM +0100, Andreas Gruenbacher wrote:
> Hello,
>
> Al Viro has reported issues with commit dd00aaeb3432 ("gfs2: Use
> GL_NOBLOCK flag for non-blocking lookups"):
>
> * First, parent can now be NULL and dereferencing it in
> gfs2_dir_check(d_inode(parent), &dentry->d_name, ip) isn't going to
> work;
>
> * Second, gfs2_dir_check() can still sleep, which breaks LOOKUP_RCU
> mode.
Looks sane, but there's another piece of fun in gfs_permission():
gl = rcu_dereference_check(ip->i_gl, !may_not_block);
if (unlikely(!gl)) {
/* inode is getting torn down, must be RCU mode */
WARN_ON_ONCE(!may_not_block);
return -ECHILD;
}
if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
int noblock = may_not_block ? GL_NOBLOCK : 0;
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
LM_FLAG_ANY | noblock, &i_gh);
if (error)
return error;
}
See the problem? In RCU mode we carefully fetch ->i_gl and verify
it's not NULL. Then we proceed to dereference it again.
IOW, these ip->i_gl below ought to be replaced with gl, or you are
risking a compiler fetching the sucker again and getting NULL this
time around.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups"
2024-02-02 4:23 ` Al Viro
@ 2024-02-02 4:34 ` Al Viro
2024-02-02 16:32 ` Andreas Gruenbacher
2024-02-02 4:59 ` Al Viro
1 sibling, 1 reply; 19+ messages in thread
From: Al Viro @ 2024-02-02 4:34 UTC (permalink / raw)
To: Andreas Gruenbacher; +Cc: gfs2
On Fri, Feb 02, 2024 at 04:23:13AM +0000, Al Viro wrote:
> On Fri, Jan 19, 2024 at 10:20:47PM +0100, Andreas Gruenbacher wrote:
> > Hello,
> >
> > Al Viro has reported issues with commit dd00aaeb3432 ("gfs2: Use
> > GL_NOBLOCK flag for non-blocking lookups"):
> >
> > * First, parent can now be NULL and dereferencing it in
> > gfs2_dir_check(d_inode(parent), &dentry->d_name, ip) isn't going to
> > work;
> >
> > * Second, gfs2_dir_check() can still sleep, which breaks LOOKUP_RCU
> > mode.
>
> Looks sane, but there's another piece of fun in gfs_permission():
> gl = rcu_dereference_check(ip->i_gl, !may_not_block);
> if (unlikely(!gl)) {
> /* inode is getting torn down, must be RCU mode */
> WARN_ON_ONCE(!may_not_block);
> return -ECHILD;
> }
> if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
> int noblock = may_not_block ? GL_NOBLOCK : 0;
> error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
> LM_FLAG_ANY | noblock, &i_gh);
> if (error)
> return error;
> }
>
> See the problem? In RCU mode we carefully fetch ->i_gl and verify
> it's not NULL. Then we proceed to dereference it again.
>
> IOW, these ip->i_gl below ought to be replaced with gl, or you are
> risking a compiler fetching the sucker again and getting NULL this
> time around.
fix breakage in gfs2_permission()
"gfs2: Use GL_NOBLOCK flag for non-blocking lookups" has
reintroduced the bug fixed in "gfs2: fix an oops in
gfs2_permission" a while ago. We still fetch ->i_gl carefully,
and check if it's NULL, but then we proceed to fetch it again.
If it became NULL in the meanwhile, we get trouble...
Trivial to fix, fortunately.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 6bfc9383b7b8..d045562bf08f 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -1882,9 +1882,9 @@ int gfs2_permission(struct mnt_idmap *idmap, struct inode *inode,
WARN_ON_ONCE(!may_not_block);
return -ECHILD;
}
- if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
+ if (gfs2_glock_is_locked_by_me(gl) == NULL) {
int noblock = may_not_block ? GL_NOBLOCK : 0;
- error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
+ error = gfs2_glock_nq_init(gl, LM_ST_SHARED,
LM_FLAG_ANY | noblock, &i_gh);
if (error)
return error;
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups"
2024-02-02 4:23 ` Al Viro
2024-02-02 4:34 ` Al Viro
@ 2024-02-02 4:59 ` Al Viro
2024-02-02 5:02 ` Al Viro
2024-02-02 17:09 ` Andreas Gruenbacher
1 sibling, 2 replies; 19+ messages in thread
From: Al Viro @ 2024-02-02 4:59 UTC (permalink / raw)
To: Andreas Gruenbacher; +Cc: gfs2
On Fri, Feb 02, 2024 at 04:23:13AM +0000, Al Viro wrote:
> if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
> int noblock = may_not_block ? GL_NOBLOCK : 0;
> error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
> LM_FLAG_ANY | noblock, &i_gh);
> if (error)
> return error;
> }
>
> See the problem? In RCU mode we carefully fetch ->i_gl and verify
> it's not NULL. Then we proceed to dereference it again.
>
> IOW, these ip->i_gl below ought to be replaced with gl, or you are
> risking a compiler fetching the sucker again and getting NULL this
> time around.
Wait, what's going on with this part?
if (gfs2_holder_initialized(&i_gh))
gfs2_glock_dq_uninit(&i_gh);
First of all, gfs2_glock_dq_uninit() can bloody well sleep in
gfs2_glock_dq(). What's more, it drops a reference to ->gh_gl,
i.e. our gl, in gfs2_holder_uninit(). Where is the matching
increment of gl's refcount?
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups"
2024-02-02 4:59 ` Al Viro
@ 2024-02-02 5:02 ` Al Viro
2024-02-02 17:09 ` Andreas Gruenbacher
1 sibling, 0 replies; 19+ messages in thread
From: Al Viro @ 2024-02-02 5:02 UTC (permalink / raw)
To: Andreas Gruenbacher; +Cc: gfs2
On Fri, Feb 02, 2024 at 04:59:44AM +0000, Al Viro wrote:
> On Fri, Feb 02, 2024 at 04:23:13AM +0000, Al Viro wrote:
>
> > if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
> > int noblock = may_not_block ? GL_NOBLOCK : 0;
> > error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
> > LM_FLAG_ANY | noblock, &i_gh);
> > if (error)
> > return error;
> > }
> >
> > See the problem? In RCU mode we carefully fetch ->i_gl and verify
> > it's not NULL. Then we proceed to dereference it again.
> >
> > IOW, these ip->i_gl below ought to be replaced with gl, or you are
> > risking a compiler fetching the sucker again and getting NULL this
> > time around.
>
> Wait, what's going on with this part?
> if (gfs2_holder_initialized(&i_gh))
> gfs2_glock_dq_uninit(&i_gh);
>
> First of all, gfs2_glock_dq_uninit() can bloody well sleep in
> gfs2_glock_dq(). What's more, it drops a reference to ->gh_gl,
> i.e. our gl, in gfs2_holder_uninit(). Where is the matching
> increment of gl's refcount?
The same applies to gfs2_drevalidate(), both before and after your
fixes. Am I missing something subtle here?
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups"
2024-02-02 4:34 ` Al Viro
@ 2024-02-02 16:32 ` Andreas Gruenbacher
0 siblings, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-02-02 16:32 UTC (permalink / raw)
To: Al Viro; +Cc: gfs2
Hi Al,
On Fri, Feb 2, 2024 at 5:34 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
> On Fri, Feb 02, 2024 at 04:23:13AM +0000, Al Viro wrote:
> > On Fri, Jan 19, 2024 at 10:20:47PM +0100, Andreas Gruenbacher wrote:
> > > Hello,
> > >
> > > Al Viro has reported issues with commit dd00aaeb3432 ("gfs2: Use
> > > GL_NOBLOCK flag for non-blocking lookups"):
> > >
> > > * First, parent can now be NULL and dereferencing it in
> > > gfs2_dir_check(d_inode(parent), &dentry->d_name, ip) isn't going to
> > > work;
> > >
> > > * Second, gfs2_dir_check() can still sleep, which breaks LOOKUP_RCU
> > > mode.
> >
> > Looks sane, but there's another piece of fun in gfs_permission():
> > gl = rcu_dereference_check(ip->i_gl, !may_not_block);
> > if (unlikely(!gl)) {
> > /* inode is getting torn down, must be RCU mode */
> > WARN_ON_ONCE(!may_not_block);
> > return -ECHILD;
> > }
> > if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
> > int noblock = may_not_block ? GL_NOBLOCK : 0;
> > error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
> > LM_FLAG_ANY | noblock, &i_gh);
> > if (error)
> > return error;
> > }
> >
> > See the problem? In RCU mode we carefully fetch ->i_gl and verify
> > it's not NULL. Then we proceed to dereference it again.
> >
> > IOW, these ip->i_gl below ought to be replaced with gl, or you are
> > risking a compiler fetching the sucker again and getting NULL this
> > time around.
>
> fix breakage in gfs2_permission()
>
> "gfs2: Use GL_NOBLOCK flag for non-blocking lookups" has
> reintroduced the bug fixed in "gfs2: fix an oops in
> gfs2_permission" a while ago. We still fetch ->i_gl carefully,
> and check if it's NULL, but then we proceed to fetch it again.
> If it became NULL in the meanwhile, we get trouble...
>
> Trivial to fix, fortunately.
Indeed, and thanks for the review.
The other problem isn't quite as easy to address, unfortunately. I've
sent Linus a revert request.
Andreas
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups"
2024-02-02 4:59 ` Al Viro
2024-02-02 5:02 ` Al Viro
@ 2024-02-02 17:09 ` Andreas Gruenbacher
1 sibling, 0 replies; 19+ messages in thread
From: Andreas Gruenbacher @ 2024-02-02 17:09 UTC (permalink / raw)
To: Al Viro; +Cc: gfs2
On Fri, Feb 2, 2024 at 5:59 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Fri, Feb 02, 2024 at 04:23:13AM +0000, Al Viro wrote:
>
> > if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
> > int noblock = may_not_block ? GL_NOBLOCK : 0;
> > error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
> > LM_FLAG_ANY | noblock, &i_gh);
> > if (error)
> > return error;
> > }
> >
> > See the problem? In RCU mode we carefully fetch ->i_gl and verify
> > it's not NULL. Then we proceed to dereference it again.
> >
> > IOW, these ip->i_gl below ought to be replaced with gl, or you are
> > risking a compiler fetching the sucker again and getting NULL this
> > time around.
>
> Wait, what's going on with this part?
> if (gfs2_holder_initialized(&i_gh))
> gfs2_glock_dq_uninit(&i_gh);
>
> First of all, gfs2_glock_dq_uninit() can bloody well sleep in
> gfs2_glock_dq().
Right, we need to get rid of the sleeping there. It's a really bad
design and I'm trying to figure out how to avoid it.
The sleeping in gfs2_glock_dq_uninit() -> gfs2_holder_uninit() ->
gfs2_glock_put() can be avoided by deferring the final put to
workqueue context; that looks pretty simple to achieve in comparison.
> What's more, it drops a reference to ->gh_gl,
> i.e. our gl, in gfs2_holder_uninit(). Where is the matching
> increment of gl's refcount?
That happens in gfs2_glock_nq_init() -> __gfs2_holder_init() ->
gfs2_glock_hold().
Thanks again,
Andreas
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2024-02-02 17:10 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-19 21:20 [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 1/9] gfs2: Fix gfs2_drevalidate NULL pointer dereference Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 2/9] gfs2: Pass FGP flags to gfs2_getbuf Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 3/9] gfs2: Split gfs2_meta_read_async off from gfs2_meta_read Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 4/9] gfs2: Add FGP_NOWAIT support to gfs2_meta_read_async Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 5/9] gfs2: Pass FGP flags to gfs2_meta_{,inode_}buffer Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 6/9] gfs2: Pass FGP flags to gfs2_dirent_search Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 7/9] gfs2: Pass FGP flags to gfs2_dir_check Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 8/9] gfs2: Minor gfs2_drevalidate cleanup Andreas Gruenbacher
2024-01-19 21:20 ` [PATCH 9/9] gfs2: Fix LOOKUP_RCU support in gfs2_drevalidate Andreas Gruenbacher
2024-01-20 1:36 ` [PATCH 0/9] gfs2: Bugs in "Use GL_NOBLOCK flag for non-blocking lookups" Al Viro
2024-01-20 1:38 ` Al Viro
2024-01-22 12:52 ` Andrew Price
2024-02-02 4:23 ` Al Viro
2024-02-02 4:34 ` Al Viro
2024-02-02 16:32 ` Andreas Gruenbacher
2024-02-02 4:59 ` Al Viro
2024-02-02 5:02 ` Al Viro
2024-02-02 17:09 ` Andreas Gruenbacher
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox