From e6bd64773aa1fae7e0fd68c3ba7112accac0cc26 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 1 May 2025 12:27:31 -0400 Subject: [PATCH] Fix unlocked pt_excl in scoutfs_readahead This caller of scoutfs_get_block is now actively used in el10 and the WARN_ON_ONCE(!lock) in data.c:567 triggers. Add the scoutfs_per_task_add_excl/del calls in scoutfs_readpage, scoutfs_readpages, and scoutfs_readahead to register the cluster lock for scoutfs_get_block_read. Add unconditionally rather than guarded by the add_excl return, since these methods can be reached reentrantly from a top-level read that already added the entry. Skipping the I/O in that case left BUG_ON(!list_empty(pages)) in scoutfs_readpages and the page locked in scoutfs_readpage. Move scoutfs_per_task_del before scoutfs_unlock to match the ordering used by file.c read/write paths. Signed-off-by: Auke Kok --- kmod/src/data.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/kmod/src/data.c b/kmod/src/data.c index 5856146f..daef52fb 100644 --- a/kmod/src/data.c +++ b/kmod/src/data.c @@ -727,24 +727,24 @@ static int scoutfs_readpage(struct file *file, struct page *page) return ret; } - if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock)) { - ret = scoutfs_data_wait_check(inode, page_offset(page), - PAGE_SIZE, SEF_OFFLINE, - SCOUTFS_IOC_DWO_READ, &dw, - inode_lock); - if (ret != 0) { - unlock_page(page); - scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); - scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ); - } - if (ret > 0) { - ret = scoutfs_data_wait(inode, &dw); - if (ret == 0) - ret = AOP_TRUNCATED_PAGE; - } - if (ret != 0) - return ret; + scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock); + + ret = scoutfs_data_wait_check(inode, page_offset(page), + PAGE_SIZE, SEF_OFFLINE, + SCOUTFS_IOC_DWO_READ, &dw, + inode_lock); + if (ret != 0) { + unlock_page(page); + scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); + scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ); } + if (ret > 0) { + ret = scoutfs_data_wait(inode, &dw); + if (ret == 0) + ret = AOP_TRUNCATED_PAGE; + } + if (ret != 0) + return ret; #ifdef KC_MPAGE_READ_FOLIO ret = mpage_read_folio(folio, scoutfs_get_block_read); @@ -752,8 +752,8 @@ static int scoutfs_readpage(struct file *file, struct page *page) ret = mpage_readpage(page, scoutfs_get_block_read); #endif - scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ); scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); + scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ); return ret; } @@ -761,8 +761,10 @@ static int scoutfs_readpage(struct file *file, struct page *page) static void scoutfs_readahead(struct readahead_control *rac) { struct inode *inode = rac->file->f_inode; + struct scoutfs_inode_info *si = SCOUTFS_I(inode); struct super_block *sb = inode->i_sb; struct scoutfs_lock *inode_lock = NULL; + SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent); int ret; ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, @@ -770,6 +772,8 @@ static void scoutfs_readahead(struct readahead_control *rac) if (ret) return; + scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock); + ret = scoutfs_data_wait_check(inode, readahead_pos(rac), readahead_length(rac), SEF_OFFLINE, SCOUTFS_IOC_DWO_READ, NULL, @@ -777,6 +781,7 @@ static void scoutfs_readahead(struct readahead_control *rac) if (ret == 0) mpage_readahead(rac, scoutfs_get_block_read); + scoutfs_per_task_del(&si->pt_data_lock, &pt_ent); scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ); }