From c24a090ec389ae9ca2bedb4c7e3ee777deb63c7f Mon Sep 17 00:00:00 2001 From: Alexander Boyko Date: Thu, 1 Jun 2023 10:19:40 -0400 Subject: [PATCH] LU-16887 scrub: delete OI when inode missing osd_iget_check() function have no ability to check OI when osd_iget() returns error, because inode is lost during error. Let's return old logic. Scrub doesn't check consistency between OI and inode for items from inconsistent list. When OI points to worng inode, OI record should be deleted. Fixes: 716de353b ("LU-15542 osd-ldiskfs: exclude EA inode from processing") HPE-bug-id: LUS-11540, LUS-11585 Signed-off-by: Alexander Boyko Change-Id: Ic1618db1c8ee24bb307a9cf3f5ca98441a739b7f Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/51263 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Vitaly Fertman Reviewed-by: Lai Siyao Reviewed-by: Oleg Drokin --- lustre/osd-ldiskfs/osd_handler.c | 46 ++++++++++++++++++++++++++-------------- lustre/osd-ldiskfs/osd_scrub.c | 21 +++++++++++++----- lustre/tests/sanity-scrub.sh | 32 ++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/lustre/osd-ldiskfs/osd_handler.c b/lustre/osd-ldiskfs/osd_handler.c index 88105ca..d0bfa4f 100644 --- a/lustre/osd-ldiskfs/osd_handler.c +++ b/lustre/osd-ldiskfs/osd_handler.c @@ -488,10 +488,11 @@ int osd_get_lma(struct osd_thread_info *info, struct inode *inode, /* * retrieve object from backend ext fs. **/ -struct inode *osd_iget(struct osd_thread_info *info, struct osd_device *dev, - struct osd_inode_id *id) +static struct inode *osd_iget2(struct osd_thread_info *info, + struct osd_device *dev, struct osd_inode_id *id, + int *err) { - int rc; + int rc = 0; struct inode *inode = NULL; /* @@ -510,32 +511,27 @@ struct inode *osd_iget(struct osd_thread_info *info, struct osd_device *dev, CDEBUG(D_INODE, "unmatched inode: ino = %u, oii_gen = %u, " "i_generation = %u\n", id->oii_ino, id->oii_gen, inode->i_generation); - iput(inode); - inode = ERR_PTR(-ESTALE); + rc = -ESTALE; } else if (inode->i_nlink == 0) { /* * due to parallel readdir and unlink, * we can have dead inode here. */ CDEBUG(D_INODE, "stale inode: ino = %u\n", id->oii_ino); - iput(inode); - inode = ERR_PTR(-ESTALE); + rc = -ESTALE; } else if (is_bad_inode(inode)) { CWARN("%s: bad inode: ino = %u: rc = %d\n", osd_dev2name(dev), id->oii_ino, -ENOENT); - iput(inode); - inode = ERR_PTR(-ENOENT); + rc = -ENOENT; } else if (osd_is_ea_inode(inode)) { /* * EA inode is internal ldiskfs object, should don't visible * on osd */ CDEBUG(D_INODE, "EA inode: ino = %u\n", id->oii_ino); - iput(inode); - inode = ERR_PTR(-ENOENT); + rc = -ENOENT; } else if ((rc = osd_attach_jinode(inode))) { - iput(inode); - inode = ERR_PTR(rc); + CDEBUG(D_INODE, "jbd: ino = %u rc = %d\n", id->oii_ino, rc); } else { ldiskfs_clear_inode_state(inode, LDISKFS_STATE_LUSTRE_DESTROY); if (id->oii_gen == OSD_OII_NOGEN) @@ -551,6 +547,25 @@ struct inode *osd_iget(struct osd_thread_info *info, struct osd_device *dev, if (!(inode->i_flags & S_NOCMTIME)) inode->i_flags |= S_NOCMTIME; } + + *err = rc; + + return inode; +} + +struct inode *osd_iget(struct osd_thread_info *info, struct osd_device *dev, + struct osd_inode_id *id) +{ + struct inode *inode; + int rc = 0; + + inode = osd_iget2(info, dev, id, &rc); + + if (rc) { + iput(inode); + inode = ERR_PTR(rc); + } + return inode; } @@ -645,9 +660,8 @@ static struct inode *osd_iget_check(struct osd_thread_info *info, */ again: - inode = osd_iget(info, dev, id); - if (IS_ERR(inode)) { - rc = PTR_ERR(inode); + inode = osd_iget2(info, dev, id, &rc); + if (rc) { if (!trusted && (rc == -ENOENT || rc == -ESTALE)) goto check_oi; diff --git a/lustre/osd-ldiskfs/osd_scrub.c b/lustre/osd-ldiskfs/osd_scrub.c index b3d5712..3a125ab 100644 --- a/lustre/osd-ldiskfs/osd_scrub.c +++ b/lustre/osd-ldiskfs/osd_scrub.c @@ -474,10 +474,20 @@ iget: break; } } else if (osd_id_eq(lid, lid2)) { - if (converted) - sf->sf_items_updated++; + /* + * OI records from request and current are the same + * checking inode generation at osd_iget() + */ + if (!inode) { + inode = osd_iget(info, dev, lid); + if (IS_ERR(inode)) + ops = DTO_INDEX_DELETE; + } - GOTO(out, rc = 0); + if (converted && !IS_ERR(inode)) { + sf->sf_items_updated++; + GOTO(out, rc = 0); + } } else { if (!scrub->os_partial_scan) { spin_lock(&scrub->os_lock); @@ -545,9 +555,10 @@ out: } else if (oii) { /* release fixed inconsistent item */ CDEBUG(D_LFSCK, - "%s: inconsistent OI "DFID" -> %u/%u fixed\n", + "%s: inconsistent OI "DFID" -> %u/%u %s\n", osd_dev2name(dev), PFID(fid), lid->oii_ino, - lid->oii_gen); + lid->oii_gen, ops == DTO_INDEX_DELETE ? + "deleted" : "fixed"); spin_lock(&scrub->os_lock); list_del_init(&oii->oii_list); spin_unlock(&scrub->os_lock); diff --git a/lustre/tests/sanity-scrub.sh b/lustre/tests/sanity-scrub.sh index 5d0d8fe..b5f8f88 100644 --- a/lustre/tests/sanity-scrub.sh +++ b/lustre/tests/sanity-scrub.sh @@ -1423,6 +1423,38 @@ test_20() { } run_test 20 "Don't trigger OI scrub for irreparable oi repeatedly" +test_21() { + [ $MDSCOUNT -lt 2 ] && skip "needs >= 2 MDTs" + [ "$mds1_FSTYPE" != "ldiskfs" ] && skip_env "ldiskfs only test" + + local timeout + local td=$TMP/$tdir + local device=$(mdsdevname 1) + + echo $device + + timeout=$(do_facet mds2 "$LCTL get_param -n \ + mdt.$FSNAME-MDT0001.recovery_time_hard") + for idx in $(seq $MDSCOUNT); do + stop mds${idx} + done + + #test reproduce simple situation, when OI record exist + #and inode was reused/wrong. Scrub should handle it. + do_facet mds1 "mkdir -p $td && mount -t ldiskfs $device $td && + rm -rf $td/update_log_dir/*; umount $td; rm -rf $td" + + for idx in $(seq $MDSCOUNT); do + start mds${idx} $(mdsdevname $idx) $MDS_MOUNT_OPTS || + error "mount mds$idx failed" + done + + wait_recovery_complete mds2 $((timeout + TIMEOUT)) + +} +run_test 21 "don't hang MDS recovery when failed to get update log" + + # restore MDS/OST size MDSSIZE=${SAVED_MDSSIZE} OSTSIZE=${SAVED_OSTSIZE} -- 1.8.3.1