From: Hongchao Zhang Date: Tue, 8 Jun 2021 21:50:34 +0000 (+0800) Subject: LU-13124 scrub: check for multiple linked file X-Git-Tag: 2.14.53~106 X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=commitdiff_plain;h=0c1ae1cb9c19f8a4f6c5a7ff6a1fd54807430795 LU-13124 scrub: check for multiple linked file The files on OSTs should have only one link, but it could have more than one link when there are some disk failures "multiply claimed block(s)" and fixed by e2fsck to clone these conflicted blocks. This patch adds the check of these multiple linked files in Scrub on OST. The name of the objects in "O" depends on the object's FID, the directory pattern is O/[FID_SEQ]/[SUB_DIR]/[FID_OID], the inodes of these multiple linked files are normal, but there is only one directroy entry compatible with the object, this patch scans all files under "O" to check whether its name is matched with its FID. Change-Id: I280a725939b037006935d47e9ef426a4a6a7b317 Signed-off-by: Hongchao Zhang Reviewed-on: https://review.whamcloud.com/37194 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Andreas Dilger Reviewed-by: Yang Sheng Reviewed-by: Oleg Drokin --- diff --git a/lustre/include/lustre_scrub.h b/lustre/include/lustre_scrub.h index 415ef85..89bf956 100644 --- a/lustre/include/lustre_scrub.h +++ b/lustre/include/lustre_scrub.h @@ -301,7 +301,8 @@ struct lustre_scrub { os_partial_scan:1, os_in_join:1, os_running:1, /* scrub thread is running */ - os_full_scrub:1; + os_full_scrub:1, + os_has_ml_file:1; }; #define INDEX_BACKUP_MAGIC_V1 0x1E41F208 diff --git a/lustre/lfsck/lfsck_internal.h b/lustre/lfsck/lfsck_internal.h index a8bf4df..e6a8a48 100644 --- a/lustre/lfsck/lfsck_internal.h +++ b/lustre/lfsck/lfsck_internal.h @@ -1044,8 +1044,9 @@ int lfsck_namespace_repair_dirent(const struct lu_env *env, struct dt_object *child, const char *name, const char *name2, __u16 type, bool update, bool dec); -int lfsck_verify_linkea(const struct lu_env *env, struct dt_object *obj, - const struct lu_name *cname, const struct lu_fid *pfid); +int lfsck_verify_linkea(const struct lu_env *env, struct lfsck_instance *lfsck, + struct dt_object *obj, const struct lu_name *cname, + const struct lu_fid *pfid); int lfsck_links_get_first(const struct lu_env *env, struct dt_object *obj, char *name, struct lu_fid *pfid); int lfsck_update_name_entry(const struct lu_env *env, @@ -1542,4 +1543,19 @@ lfsck_assistant_object_put(const struct lu_env *env, if (atomic_dec_and_test(&lso->lso_ref)) OBD_FREE_PTR(lso); } + +static inline struct thandle* +lfsck_trans_create(const struct lu_env *env, struct dt_device *dev, + struct lfsck_instance *lfsck) +{ + if (lfsck->li_bookmark_ram.lb_param & LPF_DRYRUN) { + CERROR("%s: transaction is being created in DRYRUN mode!\n", + lfsck_lfsck2name(lfsck)); + + dump_stack(); + return ERR_PTR(-EINVAL); + } + + return dt_trans_create(env, dev); +} #endif /* _LFSCK_INTERNAL_H */ diff --git a/lustre/lfsck/lfsck_layout.c b/lustre/lfsck/lfsck_layout.c index a50e259..e11968ef 100644 --- a/lustre/lfsck/lfsck_layout.c +++ b/lustre/lfsck/lfsck_layout.c @@ -1248,7 +1248,7 @@ lfsck_layout_lastid_create(const struct lu_env *env, memset(dof, 0, sizeof(*dof)); dof->dof_type = dt_mode_to_dft(S_IFREG); - th = dt_trans_create(env, dt); + th = lfsck_trans_create(env, dt, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); @@ -1363,7 +1363,7 @@ lfsck_layout_lastid_store(const struct lu_env *env, continue; } - th = dt_trans_create(env, dt); + th = lfsck_trans_create(env, dt, lfsck); if (IS_ERR(th)) { rc1 = PTR_ERR(th); CDEBUG(D_LFSCK, "%s: layout LFSCK failed to store " @@ -1611,7 +1611,7 @@ static int lfsck_layout_ins_dangling_rec(const struct lu_env *env, mutex_lock(&com->lc_sub_trace_objs[idx].lsto_mutex); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, com->lc_lfsck); if (IS_ERR(th)) GOTO(unlock, rc = PTR_ERR(th)); @@ -1667,7 +1667,7 @@ static int lfsck_layout_del_dangling_rec(const struct lu_env *env, mutex_lock(&com->lc_sub_trace_objs[idx].lsto_mutex); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, com->lc_lfsck); if (IS_ERR(th)) GOTO(unlock, rc = PTR_ERR(th)); @@ -2203,6 +2203,7 @@ static int lfsck_layout_update_lovea(const struct lu_env *env, } static int __lfsck_layout_update_pfid(const struct lu_env *env, + struct lfsck_component *com, struct dt_object *child, const struct lu_fid *pfid, const struct ost_layout *ol, __u32 offset, @@ -2225,7 +2226,7 @@ static int __lfsck_layout_update_pfid(const struct lu_env *env, ff->ff_range = cpu_to_le32(range); lfsck_buf_init(&buf, ff, sizeof(*ff)); - handle = dt_trans_create(env, dev); + handle = lfsck_trans_create(env, dev, com->lc_lfsck); if (IS_ERR(handle)) RETURN(PTR_ERR(handle)); @@ -2267,7 +2268,7 @@ static int lfsck_layout_update_pfid(const struct lu_env *env, if (IS_ERR(child)) RETURN(PTR_ERR(child)); - rc = __lfsck_layout_update_pfid(env, child, + rc = __lfsck_layout_update_pfid(env, com, child, lu_object_fid(&parent->do_lu), &rec->lor_layout, ea_off, rec->lor_layout_version, @@ -2463,7 +2464,7 @@ again: GOTO(unlock, rc); /* The 1st transaction. */ - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(unlock, rc = PTR_ERR(th)); @@ -2516,7 +2517,7 @@ again: th = NULL; /* The 2nd transaction. */ - rc = __lfsck_layout_update_pfid(env, cobj, pfid, + rc = __lfsck_layout_update_pfid(env, com, cobj, pfid, &rec->lor_layout, ea_off, rec->lor_layout_version, rec->lor_range); @@ -2663,7 +2664,7 @@ static int lfsck_layout_slave_conditional_destroy(const struct lu_env *env, if (la->la_ctime != 0) GOTO(unlock, rc = -ETXTBSY); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(unlock, rc = PTR_ERR(th)); @@ -2782,7 +2783,7 @@ static int lfsck_layout_conflict_create(const struct lu_env *env, if (lfsck_is_dryrun(com->lc_lfsck)) GOTO(unlock, rc = 0); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, com->lc_lfsck); if (IS_ERR(th)) GOTO(unlock, rc = PTR_ERR(th)); @@ -2903,7 +2904,7 @@ again: } if (!(bk->lb_param & LPF_DRYRUN)) { - handle = dt_trans_create(env, dt); + handle = lfsck_trans_create(env, dt, lfsck); if (IS_ERR(handle)) GOTO(unlock_layout, rc = PTR_ERR(handle)); @@ -3526,7 +3527,7 @@ static int __lfsck_layout_repair_dangling(const struct lu_env *env, GOTO(unlock1, rc); buf = lfsck_buf_get(env, ff, sizeof(struct filter_fid)); - handle = dt_trans_create(env, dev); + handle = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(handle)) GOTO(unlock1, rc = PTR_ERR(handle)); @@ -3767,7 +3768,7 @@ static int lfsck_layout_repair_unmatched_pair(const struct lu_env *env, buf = lfsck_buf_get(env, ff, sizeof(*ff)); - handle = dt_trans_create(env, dev); + handle = lfsck_trans_create(env, dev, com->lc_lfsck); if (IS_ERR(handle)) GOTO(unlock1, rc = PTR_ERR(handle)); @@ -3895,7 +3896,7 @@ static int lfsck_layout_repair_multiple_references(const struct lu_env *env, memset(dof, 0, sizeof(*dof)); dev = lfsck_obj2dev(child); - handle = dt_trans_create(env, dev); + handle = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(handle)) GOTO(log, rc = PTR_ERR(handle)); @@ -3936,7 +3937,7 @@ static int lfsck_layout_repair_multiple_references(const struct lu_env *env, if (IS_ERR(parent)) GOTO(log, rc = PTR_ERR(parent)); - handle = dt_trans_create(env, dev); + handle = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(handle)) GOTO(log, rc = PTR_ERR(handle)); @@ -4058,7 +4059,7 @@ static int lfsck_layout_repair_owner(const struct lu_env *env, tla->la_uid = pla->la_uid; tla->la_gid = pla->la_gid; tla->la_valid = LA_UID | LA_GID; - handle = dt_trans_create(env, dev); + handle = lfsck_trans_create(env, dev, com->lc_lfsck); if (IS_ERR(handle)) GOTO(log, rc = PTR_ERR(handle)); @@ -5171,7 +5172,8 @@ static int lfsck_layout_slave_repair_pfid(const struct lu_env *env, lfsck_is_dead_obj(obj))) GOTO(unlock, rc = 0); - rc = __lfsck_layout_update_pfid(env, obj, &lrl->lrl_ff_client.ff_parent, + rc = __lfsck_layout_update_pfid(env, com, obj, + &lrl->lrl_ff_client.ff_parent, &lrl->lrl_ff_client.ff_layout, lrl->lrl_ff_client.ff_layout_version, lrl->lrl_ff_client.ff_range, @@ -5754,7 +5756,7 @@ fix: if (rc != 0) GOTO(out, rc); - handle = dt_trans_create(env, dev); + handle = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(handle)) GOTO(out, rc = PTR_ERR(handle)); @@ -7108,6 +7110,7 @@ static int lfsck_fid_match_idx(const struct lu_env *env, } static void lfsck_layout_destroy_orphan(const struct lu_env *env, + struct lfsck_instance *lfsck, struct dt_object *obj) { struct dt_device *dev = lfsck_obj2dev(obj); @@ -7115,7 +7118,7 @@ static void lfsck_layout_destroy_orphan(const struct lu_env *env, int rc; ENTRY; - handle = dt_trans_create(env, dev); + handle = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(handle)) RETURN_EXIT; @@ -7448,7 +7451,7 @@ again1: * OST-object there. Destroy it now! */ if (unlikely(!(la->la_mode & S_ISUID))) { dt_read_unlock(env, obj); - lfsck_layout_destroy_orphan(env, obj); + lfsck_layout_destroy_orphan(env, lfsck, obj); lfsck_object_put(env, obj); pos++; goto again1; diff --git a/lustre/lfsck/lfsck_lib.c b/lustre/lfsck/lfsck_lib.c index 21cd6f8..891caba 100644 --- a/lustre/lfsck/lfsck_lib.c +++ b/lustre/lfsck/lfsck_lib.c @@ -621,7 +621,7 @@ static int lfsck_lpf_remove_name_entry(const struct lu_env *env, if (rc != 0) RETURN(rc); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(unlock, rc = PTR_ERR(th)); @@ -687,7 +687,7 @@ static int lfsck_create_lpf_local(const struct lu_env *env, if (rc != 0) RETURN(rc); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) RETURN(PTR_ERR(th)); @@ -865,7 +865,7 @@ static int lfsck_create_lpf_remote(const struct lu_env *env, /* Transaction I: locally */ dev = lfsck_obj2dev(child); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) RETURN(PTR_ERR(th)); @@ -962,7 +962,7 @@ static int lfsck_create_lpf_remote(const struct lu_env *env, /* Transaction II: remotely */ dev = lfsck_obj2dev(parent); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) RETURN(PTR_ERR(th)); @@ -1280,7 +1280,7 @@ static int lfsck_verify_lpf_pairs(const struct lu_env *env, } cname = lfsck_name_get_const(env, name, strlen(name)); - rc = lfsck_verify_linkea(env, child, cname, &LU_LPF_FID); + rc = lfsck_verify_linkea(env, lfsck, child, cname, &LU_LPF_FID); if (rc == 0) rc = lfsck_update_lpf_entry(env, lfsck, parent, child, name, type); @@ -1515,7 +1515,8 @@ find_child1: } cname = lfsck_name_get_const(env, name, strlen(name)); - rc = lfsck_verify_linkea(env, child2, cname, &LU_LPF_FID); + rc = lfsck_verify_linkea(env, lfsck, child2, cname, + &LU_LPF_FID); GOTO(put, rc); } @@ -3735,7 +3736,7 @@ int lfsck_register(const struct lu_env *env, struct dt_device *key, cname = lfsck_name_get_const(env, dotlustre, strlen(dotlustre)); - rc = lfsck_verify_linkea(env, obj, cname, + rc = lfsck_verify_linkea(env, lfsck, obj, cname, &lfsck->li_global_root_fid); if (rc != 0) GOTO(out, rc); @@ -3755,7 +3756,7 @@ int lfsck_register(const struct lu_env *env, struct dt_device *key, cname = lfsck_name_get_const(env, lostfound, strlen(lostfound)); - rc = lfsck_verify_linkea(env, obj, cname, pfid); + rc = lfsck_verify_linkea(env, lfsck, obj, cname, pfid); if (rc != 0) GOTO(out, rc); diff --git a/lustre/lfsck/lfsck_namespace.c b/lustre/lfsck/lfsck_namespace.c index cd25883..4c78efa 100644 --- a/lustre/lfsck/lfsck_namespace.c +++ b/lustre/lfsck/lfsck_namespace.c @@ -531,7 +531,7 @@ int lfsck_namespace_trace_update(const struct lu_env *env, GOTO(log, rc); } - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); @@ -695,7 +695,7 @@ static int lfsck_namespace_links_remove(const struct lu_env *env, LASSERT(dt_object_remote(obj) == 0); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); @@ -990,7 +990,7 @@ again: lfsck_buf_init(&linkea_buf, ldata2.ld_buf->lb_buf, ldata2.ld_leh->leh_len); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); @@ -1122,7 +1122,7 @@ static int lfsck_lmv_set(const struct lu_env *env, ENTRY; - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) RETURN(PTR_ERR(th)); @@ -1155,7 +1155,7 @@ static int lfsck_lmv_delete(const struct lu_env *env, ENTRY; - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) RETURN(PTR_ERR(th)); @@ -1320,7 +1320,7 @@ static int lfsck_namespace_insert_normal(const struct lu_env *env, if (unlikely(!dt_try_as_dir(env, parent))) GOTO(unlock, rc = -ENOTDIR); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(unlock, rc = PTR_ERR(th)); @@ -1538,7 +1538,7 @@ again: if (rc != 0) GOTO(unlock1, rc); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(unlock1, rc = PTR_ERR(th)); @@ -1730,7 +1730,7 @@ static int lfsck_namespace_shrink_linkea(const struct lu_env *env, } again: - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(unlock1, rc = PTR_ERR(th)); @@ -2045,7 +2045,7 @@ replace: if (lfsck->li_bookmark_ram.lb_param & LPF_DRYRUN) GOTO(log, rc = 1); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); @@ -2131,7 +2131,7 @@ int lfsck_namespace_rebuild_linkea(const struct lu_env *env, int rc = 0; ENTRY; - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); @@ -2237,7 +2237,7 @@ int lfsck_namespace_repair_dirent(const struct lu_env *env, if (rc != 0) GOTO(log, rc); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(unlock1, rc = PTR_ERR(th)); @@ -2384,7 +2384,7 @@ static int lfsck_namespace_repair_unmatched_pairs(const struct lu_env *env, lfsck_buf_init(&linkea_buf, ldata.ld_buf->lb_buf, ldata.ld_leh->leh_len); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); @@ -3107,7 +3107,7 @@ static int lfsck_namespace_repair_nlink(const struct lu_env *env, if (rc != 0) GOTO(log, rc); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); @@ -3467,7 +3467,7 @@ static int lfsck_namespace_linkea_clear_overflow(const struct lu_env *env, if (rc != 0) GOTO(log, rc); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); @@ -3606,7 +3606,7 @@ static int lfsck_namespace_check_agent_entry(const struct lu_env *env, if (rc) GOTO(out, rc); - handle = dt_trans_create(env, dev); + handle = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(handle)) GOTO(unlock, rc = PTR_ERR(handle)); @@ -5388,7 +5388,7 @@ int lfsck_namespace_repair_dangling(const struct lu_env *env, * the MDT-object without stripes (dof->dof_reg.striped = 0). related * OST-objects will be created when write open. */ - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(unlock_child, rc = PTR_ERR(th)); @@ -5730,7 +5730,7 @@ again: if (rc != 0) GOTO(out, rc); - handle = dt_trans_create(env, dev); + handle = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(handle)) GOTO(out, rc = PTR_ERR(handle)); @@ -6141,7 +6141,7 @@ static int lfsck_namespace_scan_local_lpf_one(const struct lu_env *env, GOTO(out, rc); } - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(out, rc = PTR_ERR(th)); @@ -6870,8 +6870,9 @@ const struct lfsck_assistant_operations lfsck_namespace_assistant_ops = { * \retval 0 for success * \retval negative error number on failure */ -int lfsck_verify_linkea(const struct lu_env *env, struct dt_object *obj, - const struct lu_name *cname, const struct lu_fid *pfid) +int lfsck_verify_linkea(const struct lu_env *env, struct lfsck_instance *lfsck, + struct dt_object *obj, const struct lu_name *cname, + const struct lu_fid *pfid) { struct dt_device *dev = lfsck_obj2dev(obj); struct linkea_data ldata = { NULL }; @@ -6908,7 +6909,7 @@ int lfsck_verify_linkea(const struct lu_env *env, struct dt_object *obj, lfsck_buf_init(&linkea_buf, ldata.ld_buf->lb_buf, ldata.ld_leh->leh_len); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) RETURN(PTR_ERR(th)); @@ -7002,7 +7003,7 @@ int lfsck_update_name_entry(const struct lu_env *env, if (rc != 0) RETURN(rc); - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(unlock, rc = PTR_ERR(th)); diff --git a/lustre/lfsck/lfsck_striped_dir.c b/lustre/lfsck/lfsck_striped_dir.c index 2980277..63a8754 100644 --- a/lustre/lfsck/lfsck_striped_dir.c +++ b/lustre/lfsck/lfsck_striped_dir.c @@ -208,7 +208,7 @@ static int lfsck_disable_master_lmv(const struct lu_env *env, int rc = 0; ENTRY; - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); @@ -1085,7 +1085,7 @@ int lfsck_namespace_update_lmv(const struct lu_env *env, GOTO(log, rc); } - th = dt_trans_create(env, dev); + th = lfsck_trans_create(env, dev, lfsck); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); diff --git a/lustre/osd-ldiskfs/osd_compat.c b/lustre/osd-ldiskfs/osd_compat.c index 8c95620..7c8aee1 100644 --- a/lustre/osd-ldiskfs/osd_compat.c +++ b/lustre/osd-ldiskfs/osd_compat.c @@ -826,10 +826,9 @@ out: return rc; } -static int osd_obj_del_entry(struct osd_thread_info *info, - struct osd_device *osd, - struct dentry *dird, char *name, - handle_t *th) +int osd_obj_del_entry(struct osd_thread_info *info, struct osd_device *osd, + struct dentry *dird, char *name, int namelen, + handle_t *th) { struct ldiskfs_dir_entry_2 *de; struct buffer_head *bh; @@ -845,7 +844,7 @@ static int osd_obj_del_entry(struct osd_thread_info *info, child = &info->oti_child_dentry; child->d_name.hash = 0; child->d_name.name = name; - child->d_name.len = strlen(name); + child->d_name.len = namelen; child->d_parent = dird; child->d_inode = NULL; @@ -928,15 +927,6 @@ static inline void osd_seq_name(char *seq_name, size_t name_size, u64 seq) fid_seq_is_idif(seq) ? 0 : seq); } -static inline void osd_oid_name(char *name, size_t name_size, - const struct lu_fid *fid, u64 id) -{ - snprintf(name, name_size, - (fid_seq_is_rsvd(fid_seq(fid)) || - fid_seq_is_mdt0(fid_seq(fid)) || - fid_seq_is_idif(fid_seq(fid))) ? "%llu" : "%llx", id); -} - /* external locking is required */ static int osd_seq_load_locked(struct osd_thread_info *info, struct osd_device *osd, @@ -1005,8 +995,8 @@ out_err: RETURN(rc); } -static struct osd_obj_seq *osd_seq_load(struct osd_thread_info *info, - struct osd_device *osd, u64 seq) +struct osd_obj_seq *osd_seq_load(struct osd_thread_info *info, + struct osd_device *osd, u64 seq) { struct osd_obj_map *map; struct osd_obj_seq *osd_seq; @@ -1200,7 +1190,7 @@ int osd_obj_map_delete(struct osd_thread_info *info, struct osd_device *osd, LASSERT(d); osd_oid_name(name, sizeof(name), fid, ostid_id(ostid)); - rc = osd_obj_del_entry(info, osd, d, name, th); + rc = osd_obj_del_entry(info, osd, d, name, strlen(name), th); cleanup: RETURN(rc); } diff --git a/lustre/osd-ldiskfs/osd_handler.c b/lustre/osd-ldiskfs/osd_handler.c index 2c6741a..071ae6b 100644 --- a/lustre/osd-ldiskfs/osd_handler.c +++ b/lustre/osd-ldiskfs/osd_handler.c @@ -6602,36 +6602,23 @@ static const struct dt_index_operations osd_index_iam_ops = { } }; - -/** - * Creates or initializes iterator context. - * - * \retval struct osd_it_ea, iterator structure on success - * - */ -static struct dt_it *osd_it_ea_init(const struct lu_env *env, - struct dt_object *dt, - __u32 attr) +struct osd_it_ea *osd_it_dir_init(const struct lu_env *env, + struct inode *inode, __u32 attr) { - struct osd_object *obj = osd_dt_obj(dt); struct osd_thread_info *info = osd_oti_get(env); struct osd_it_ea *oie; struct file *file; - struct lu_object *lo = &dt->do_lu; struct dentry *obj_dentry; ENTRY; - if (!dt_object_exists(dt) || obj->oo_destroyed) - RETURN(ERR_PTR(-ENOENT)); - OBD_SLAB_ALLOC_PTR_GFP(oie, osd_itea_cachep, GFP_NOFS); if (oie == NULL) RETURN(ERR_PTR(-ENOMEM)); obj_dentry = &oie->oie_dentry; - obj_dentry->d_inode = obj->oo_inode; - obj_dentry->d_sb = osd_sb(osd_obj2dev(obj)); + obj_dentry->d_inode = inode; + obj_dentry->d_sb = inode->i_sb; obj_dentry->d_name.hash = 0; oie->oie_rd_dirent = 0; @@ -6645,7 +6632,7 @@ static struct dt_it *osd_it_ea_init(const struct lu_env *env, if (oie->oie_buf == NULL) RETURN(ERR_PTR(-ENOMEM)); } - oie->oie_obj = obj; + oie->oie_obj = NULL; file = &oie->oie_file; @@ -6655,14 +6642,56 @@ static struct dt_it *osd_it_ea_init(const struct lu_env *env, else file->f_mode = FMODE_32BITHASH; file->f_path.dentry = obj_dentry; - file->f_mapping = obj->oo_inode->i_mapping; - file->f_op = obj->oo_inode->i_fop; - file->f_inode = obj->oo_inode; + file->f_mapping = inode->i_mapping; + file->f_op = inode->i_fop; + file->f_inode = inode; + + RETURN(oie); +} +/** + * Creates or initializes iterator context. + * + * \retval struct osd_it_ea, iterator structure on success + * + */ +static struct dt_it *osd_it_ea_init(const struct lu_env *env, + struct dt_object *dt, + __u32 attr) +{ + struct osd_object *obj = osd_dt_obj(dt); + struct lu_object *lo = &dt->do_lu; + struct osd_it_ea *oie; + + ENTRY; + + if (!dt_object_exists(dt) || obj->oo_destroyed) + RETURN(ERR_PTR(-ENOENT)); + + oie = osd_it_dir_init(env, obj->oo_inode, attr); + if (IS_ERR(oie)) + RETURN((struct dt_it *)oie); + + oie->oie_obj = obj; lu_object_get(lo); RETURN((struct dt_it *)oie); } +void osd_it_dir_fini(const struct lu_env *env, struct osd_it_ea *oie, + struct inode *inode) +{ + struct osd_thread_info *info = osd_oti_get(env); + + ENTRY; + oie->oie_file.f_op->release(inode, &oie->oie_file); + if (unlikely(oie->oie_buf != info->oti_it_ea_buf)) + OBD_FREE(oie->oie_buf, OSD_IT_EA_BUFSIZE); + else + info->oti_it_ea_buf_used = 0; + OBD_SLAB_FREE_PTR(oie, osd_itea_cachep); + EXIT; +} + /** * Destroy or finishes iterator context. * @@ -6670,19 +6699,13 @@ static struct dt_it *osd_it_ea_init(const struct lu_env *env, */ static void osd_it_ea_fini(const struct lu_env *env, struct dt_it *di) { - struct osd_thread_info *info = osd_oti_get(env); struct osd_it_ea *oie = (struct osd_it_ea *)di; struct osd_object *obj = oie->oie_obj; struct inode *inode = obj->oo_inode; ENTRY; - oie->oie_file.f_op->release(inode, &oie->oie_file); + osd_it_dir_fini(env, (struct osd_it_ea *)di, inode); osd_object_put(env, obj); - if (unlikely(oie->oie_buf != info->oti_it_ea_buf)) - OBD_FREE(oie->oie_buf, OSD_IT_EA_BUFSIZE); - else - info->oti_it_ea_buf_used = 0; - OBD_SLAB_FREE_PTR(oie, osd_itea_cachep); EXIT; } @@ -6761,7 +6784,8 @@ static int osd_ldiskfs_filldir(void *buf, /* "." is just the object itself. */ if (namelen == 1 && name[0] == '.') { - *fid = obj->oo_dt.do_lu.lo_header->loh_fid; + if (obj != NULL) + *fid = obj->oo_dt.do_lu.lo_header->loh_fid; } else if (d_type & LDISKFS_DIRENT_LUFID) { rec = (struct osd_fid_pack *)(name + namelen + 1); if (osd_fid_unpack(fid, rec) != 0) @@ -6772,7 +6796,8 @@ static int osd_ldiskfs_filldir(void *buf, d_type &= ~LDISKFS_DIRENT_LUFID; /* NOT export local root. */ - if (unlikely(osd_sb(osd_obj2dev(obj))->s_root->d_inode->i_ino == ino)) { + if (obj != NULL && + unlikely(osd_sb(osd_obj2dev(obj))->s_root->d_inode->i_ino == ino)) { ino = obj->oo_inode->i_ino; *fid = obj->oo_dt.do_lu.lo_header->loh_fid; } @@ -6799,12 +6824,10 @@ static int osd_ldiskfs_filldir(void *buf, * \retval -ve on error * \retval +1 reach the end of entry */ -static int osd_ldiskfs_it_fill(const struct lu_env *env, - const struct dt_it *di) +int osd_ldiskfs_it_fill(const struct lu_env *env, const struct dt_it *di) { struct osd_it_ea *it = (struct osd_it_ea *)di; struct osd_object *obj = it->oie_obj; - struct inode *inode = obj->oo_inode; struct htree_lock *hlock = NULL; struct file *filp = &it->oie_file; int rc = 0; @@ -6817,12 +6840,15 @@ static int osd_ldiskfs_it_fill(const struct lu_env *env, it->oie_dirent = it->oie_buf; it->oie_rd_dirent = 0; - if (obj->oo_hl_head != NULL) { - hlock = osd_oti_get(env)->oti_hlock; - ldiskfs_htree_lock(hlock, obj->oo_hl_head, - inode, LDISKFS_HLOCK_READDIR); - } else { - down_read(&obj->oo_ext_idx_sem); + if (obj) { + if (obj->oo_hl_head != NULL) { + hlock = osd_oti_get(env)->oti_hlock; + ldiskfs_htree_lock(hlock, obj->oo_hl_head, + obj->oo_inode, + LDISKFS_HLOCK_READDIR); + } else { + down_read(&obj->oo_ext_idx_sem); + } } filp->f_cred = current_cred(); @@ -6849,10 +6875,12 @@ static int osd_ldiskfs_it_fill(const struct lu_env *env, it->oie_it_dirent = 1; } unlock: - if (hlock != NULL) - ldiskfs_htree_unlock(hlock); - else - up_read(&obj->oo_ext_idx_sem); + if (obj) { + if (hlock != NULL) + ldiskfs_htree_unlock(hlock); + else + up_read(&obj->oo_ext_idx_sem); + } RETURN(rc); } diff --git a/lustre/osd-ldiskfs/osd_internal.h b/lustre/osd-ldiskfs/osd_internal.h index 0240260..11f8a6e 100644 --- a/lustre/osd-ldiskfs/osd_internal.h +++ b/lustre/osd-ldiskfs/osd_internal.h @@ -711,6 +711,9 @@ struct osd_thread_info { struct page **oti_dio_pages; int oti_dio_pages_used; + + struct osd_it_ea_dirent *oti_seq_dirent; + struct osd_it_ea_dirent *oti_dir_dirent; }; extern int ldiskfs_pdo; @@ -745,6 +748,17 @@ static inline int __osd_xattr_set(struct osd_thread_info *info, return ll_vfs_setxattr(dentry, inode, name, buf, buflen, fl); } +static inline char *osd_oid_name(char *name, size_t name_size, + const struct lu_fid *fid, u64 id) +{ + snprintf(name, name_size, + (fid_seq_is_rsvd(fid_seq(fid)) || + fid_seq_is_mdt0(fid_seq(fid)) || + fid_seq_is_idif(fid_seq(fid))) ? "%llu" : "%llx", id); + + return name; +} + #ifdef CONFIG_PROC_FS /* osd_lproc.c */ extern struct lprocfs_vars lprocfs_osd_obd_vars[]; @@ -765,12 +779,19 @@ osd_iget_fid(struct osd_thread_info *info, struct osd_device *dev, struct osd_inode_id *id, struct lu_fid *fid); int osd_ea_fid_set(struct osd_thread_info *info, struct inode *inode, const struct lu_fid *fid, __u32 compat, __u32 incompat); +struct osd_obj_seq *osd_seq_load(struct osd_thread_info *info, + struct osd_device *osd, u64 seq); int osd_get_lma(struct osd_thread_info *info, struct inode *inode, struct dentry *dentry, struct lustre_ost_attrs *loa); void osd_add_oi_cache(struct osd_thread_info *info, struct osd_device *osd, struct osd_inode_id *id, const struct lu_fid *fid); int osd_get_idif(struct osd_thread_info *info, struct inode *inode, struct dentry *dentry, struct lu_fid *fid); +struct osd_it_ea *osd_it_dir_init(const struct lu_env *env, struct inode *inode, + __u32 attr); +void osd_it_dir_fini(const struct lu_env *env, struct osd_it_ea *oie, + struct inode *inode); +int osd_ldiskfs_it_fill(const struct lu_env *env, const struct dt_it *di); int osd_obj_map_init(const struct lu_env *env, struct osd_device *osd); void osd_obj_map_fini(struct osd_device *dev); @@ -796,6 +817,9 @@ int osd_obj_spec_insert(struct osd_thread_info *info, struct osd_device *osd, int osd_obj_spec_update(struct osd_thread_info *info, struct osd_device *osd, const struct lu_fid *fid, const struct osd_inode_id *id, handle_t *th); +int osd_obj_del_entry(struct osd_thread_info *info, struct osd_device *osd, + struct dentry *dird, char *name, int namelen, + handle_t *th); char *osd_lf_fid2name(const struct lu_fid *fid); int osd_scrub_start(const struct lu_env *env, struct osd_device *dev, diff --git a/lustre/osd-ldiskfs/osd_scrub.c b/lustre/osd-ldiskfs/osd_scrub.c index b541f5c..2a0ed36 100644 --- a/lustre/osd-ldiskfs/osd_scrub.c +++ b/lustre/osd-ldiskfs/osd_scrub.c @@ -751,6 +751,9 @@ static int osd_iit_iget(struct osd_thread_info *info, struct osd_device *dev, RETURN(rc); } + if (dev->od_is_ost && S_ISREG(inode->i_mode) && inode->i_nlink > 1) + dev->od_scrub.os_scrub.os_has_ml_file = 1; + /* It is an EA inode, no OI mapping for it, skip it. */ if (osd_is_ea_inode(inode)) GOTO(put, rc = SCRUB_NEXT_CONTINUE); @@ -1256,12 +1259,15 @@ static int osd_otable_it_preload(const struct lu_env *env, RETURN(rc < 0 ? rc : ooc->ooc_cached_items); } +static int osd_scan_ml_file_main(const struct lu_env *env, + struct osd_device *dev); + static int osd_scrub_main(void *args) { struct lu_env env; struct osd_device *dev = (struct osd_device *)args; struct lustre_scrub *scrub = &dev->od_scrub.os_scrub; - int rc; + int rc, ret; ENTRY; rc = lu_env_init(&env, LCT_LOCAL | LCT_DT_THREAD); @@ -1302,6 +1308,12 @@ static int osd_scrub_main(void *args) GOTO(out, rc = -EINVAL); } + if (scrub->os_has_ml_file) { + ret = osd_scan_ml_file_main(&env, dev); + if (ret != 0) + rc = ret; + } + GOTO(post, rc); post: @@ -1318,6 +1330,7 @@ out: list_del_init(&oii->oii_list); OBD_FREE_PTR(oii); } + lu_env_fini(&env); noenv: @@ -3110,3 +3123,208 @@ void osd_scrub_dump(struct seq_file *m, struct osd_device *dev) scrub->os_lf_repaired, scrub->os_lf_failed); } + +typedef int (*scan_dir_helper_t)(const struct lu_env *env, + struct osd_device *dev, struct inode *dir, + struct osd_it_ea *oie); + +static int osd_scan_dir(const struct lu_env *env, struct osd_device *dev, + struct inode *inode, scan_dir_helper_t cb) +{ + struct osd_it_ea *oie; + int rc; + + ENTRY; + + oie = osd_it_dir_init(env, inode, LUDA_TYPE); + if (IS_ERR(oie)) + RETURN(PTR_ERR(oie)); + + oie->oie_file.f_pos = 0; + rc = osd_ldiskfs_it_fill(env, (struct dt_it *)oie); + if (rc > 0) + rc = -ENODATA; + if (rc) + GOTO(out, rc); + + while (oie->oie_it_dirent <= oie->oie_rd_dirent) { + if (!name_is_dot_or_dotdot(oie->oie_dirent->oied_name, + oie->oie_dirent->oied_namelen)) + cb(env, dev, inode, oie); + + oie->oie_dirent = (void *)oie->oie_dirent + + cfs_size_round(sizeof(struct osd_it_ea_dirent) + + oie->oie_dirent->oied_namelen); + + oie->oie_it_dirent++; + if (oie->oie_it_dirent <= oie->oie_rd_dirent) + continue; + + if (oie->oie_file.f_pos == + ldiskfs_get_htree_eof(&oie->oie_file)) + break; + + rc = osd_ldiskfs_it_fill(env, (struct dt_it *)oie); + if (rc) { + if (rc > 0) + rc = 0; + break; + } + } + +out: + osd_it_dir_fini(env, oie, inode); + RETURN(rc); +} + +static int osd_remove_ml_file(struct osd_thread_info *info, + struct osd_device *dev, struct inode *dir, + struct inode *inode, struct osd_it_ea *oie) +{ + handle_t *th; + struct lustre_scrub *scrub = &dev->od_scrub.os_scrub; + struct dentry *dentry; + int rc; + + ENTRY; + + if (scrub->os_file.sf_param & SP_DRYRUN) + RETURN(0); + + th = osd_journal_start_sb(osd_sb(dev), LDISKFS_HT_MISC, + osd_dto_credits_noquota[DTO_INDEX_DELETE] + + osd_dto_credits_noquota[DTO_ATTR_SET_BASE]); + if (IS_ERR(th)) + RETURN(PTR_ERR(th)); + + dentry = &oie->oie_dentry; + dentry->d_inode = dir; + dentry->d_sb = dir->i_sb; + rc = osd_obj_del_entry(info, dev, dentry, oie->oie_dirent->oied_name, + oie->oie_dirent->oied_namelen, th); + drop_nlink(inode); + mark_inode_dirty(inode); + ldiskfs_journal_stop(th); + RETURN(rc); +} + +static int osd_scan_ml_file(const struct lu_env *env, struct osd_device *dev, + struct inode *dir, struct osd_it_ea *oie) +{ + struct osd_thread_info *info = osd_oti_get(env); + struct osd_inode_id id; + struct inode *inode; + struct osd_obj_seq *oseq; + struct ost_id *ostid = &info->oti_ostid; + struct lu_fid *fid = &oie->oie_dirent->oied_fid; + char name[32]; + int dirn, rc = 0; + + ENTRY; + + osd_id_gen(&id, oie->oie_dirent->oied_ino, OSD_OII_NOGEN); + + if (!fid_is_sane(fid)) + inode = osd_iget_fid(info, dev, &id, fid); + else + inode = osd_iget(info, dev, &id); + + if (IS_ERR(inode)) + RETURN(PTR_ERR(inode)); + + fid_to_ostid(fid, ostid); + oseq = osd_seq_load(info, dev, ostid_seq(ostid)); + if (IS_ERR(oseq)) + RETURN(PTR_ERR(oseq)); + + dirn = ostid_id(ostid) & (oseq->oos_subdir_count - 1); + LASSERT(oseq->oos_dirs[dirn] != NULL); + + osd_oid_name(name, sizeof(name), fid, ostid_id(ostid)); + if (((strlen(oseq->oos_root->d_name.name) != + info->oti_seq_dirent->oied_namelen) || + strncmp(oseq->oos_root->d_name.name, + info->oti_seq_dirent->oied_name, + info->oti_seq_dirent->oied_namelen) != 0) || + ((strlen(oseq->oos_dirs[dirn]->d_name.name) != + info->oti_dir_dirent->oied_namelen) || + strncmp(oseq->oos_dirs[dirn]->d_name.name, + info->oti_dir_dirent->oied_name, + info->oti_dir_dirent->oied_namelen) != 0) || + ((strlen(name) != oie->oie_dirent->oied_namelen) || + strncmp(oie->oie_dirent->oied_name, name, + oie->oie_dirent->oied_namelen) != 0)) { + CDEBUG(D_LFSCK, "%s: the file O/%s/%s/%s is corrupted\n", + osd_name(dev), info->oti_seq_dirent->oied_name, + info->oti_dir_dirent->oied_name, + oie->oie_dirent->oied_name); + + rc = osd_remove_ml_file(info, dev, dir, inode, oie); + } + + iput(inode); + RETURN(rc); +} + +static int osd_scan_ml_file_dir(const struct lu_env *env, + struct osd_device *dev, struct inode *dir, + struct osd_it_ea *oie) +{ + struct osd_thread_info *info = osd_oti_get(env); + struct inode *inode; + struct osd_inode_id id; + int rc; + + ENTRY; + + osd_id_gen(&id, oie->oie_dirent->oied_ino, OSD_OII_NOGEN); + inode = osd_iget(info, dev, &id); + if (IS_ERR(inode)) + RETURN(PTR_ERR(inode)); + + if (!S_ISDIR(inode->i_mode)) + GOTO(out, rc = 0); + + info->oti_dir_dirent = oie->oie_dirent; + rc = osd_scan_dir(env, dev, inode, osd_scan_ml_file); + info->oti_dir_dirent = NULL; + +out: + iput(inode); + RETURN(rc); +} + +static int osd_scan_ml_file_seq(const struct lu_env *env, + struct osd_device *dev, struct inode *dir, + struct osd_it_ea *oie) +{ + struct osd_thread_info *info = osd_oti_get(env); + struct inode *inode; + struct osd_inode_id id; + int rc; + + ENTRY; + + osd_id_gen(&id, oie->oie_dirent->oied_ino, OSD_OII_NOGEN); + inode = osd_iget(info, dev, &id); + if (IS_ERR(inode)) + RETURN(PTR_ERR(inode)); + + if (!S_ISDIR(inode->i_mode)) + GOTO(out, rc = 0); + + info->oti_seq_dirent = oie->oie_dirent; + rc = osd_scan_dir(env, dev, inode, osd_scan_ml_file_dir); + info->oti_seq_dirent = NULL; + +out: + iput(inode); + RETURN(rc); +} + +static int osd_scan_ml_file_main(const struct lu_env *env, + struct osd_device *dev) +{ + return osd_scan_dir(env, dev, dev->od_ost_map->om_root->d_inode, + osd_scan_ml_file_seq); +} diff --git a/lustre/osd-zfs/osd_internal.h b/lustre/osd-zfs/osd_internal.h index dc22021..9fb947b 100644 --- a/lustre/osd-zfs/osd_internal.h +++ b/lustre/osd-zfs/osd_internal.h @@ -127,6 +127,23 @@ enum osd_zap_pos { OZI_POS_REAL = 3, /* cursor at real entries */ }; +/* + * regular ZFS direntry + */ +struct zpl_direntry { + uint64_t zde_dnode:48, + zde_pad:12, + zde_type:4; +} __attribute__((packed)); + +/* + * lustre direntry adds a fid to regular ZFS direntry + */ +struct luz_direntry { + struct zpl_direntry lzd_reg; + struct lu_fid lzd_fid; +} __attribute__((packed)); + /** * Iterator's in-memory data structure for ZAPs * @@ -139,7 +156,9 @@ struct osd_zap_it { struct osd_object *ozi_obj; unsigned ozi_reset:1; /* 1 -- no need to advance */ /* ozi_pos - position of the cursor */ - enum osd_zap_pos ozi_pos; + enum osd_zap_pos ozi_pos; + struct luz_direntry ozi_zde; + zap_attribute_t ozi_za; union { char ozi_name[MAXNAMELEN]; /* file name for dir */ __u64 ozi_key; /* binary key for index files */ @@ -147,24 +166,6 @@ struct osd_zap_it { }; #define DT_IT2DT(it) (&((struct osd_zap_it *)it)->ozi_obj->oo_dt) -/* - * regular ZFS direntry - */ -struct zpl_direntry { - uint64_t zde_dnode:48, - zde_pad:12, - zde_type:4; -} __attribute__((packed)); - -/* - * lustre direntry adds a fid to regular ZFS direntry - */ -struct luz_direntry { - struct zpl_direntry lzd_reg; - struct lu_fid lzd_fid; -} __attribute__((packed)); - - /* cached SA attributes */ struct osa_attr { uint64_t mode; @@ -267,6 +268,9 @@ struct osd_thread_info { struct lu_buf oti_xattr_lbuf; zap_cursor_t oti_zc; zap_cursor_t oti_zc2; + + char *oti_seq_name; + char *oti_dir_name; }; extern struct lu_context_key osd_key; diff --git a/lustre/osd-zfs/osd_scrub.c b/lustre/osd-zfs/osd_scrub.c index f8fb843..247973a 100644 --- a/lustre/osd-zfs/osd_scrub.c +++ b/lustre/osd-zfs/osd_scrub.c @@ -243,6 +243,29 @@ update: GOTO(out, rc); out: + if (dev->od_is_ost) { + sa_handle_t *hdl; + uint64_t nlink, mode; + + rc = -sa_handle_get(dev->od_os, oid, NULL, SA_HDL_PRIVATE, + &hdl); + if (rc) + GOTO(cleanup, rc); + + rc = -sa_lookup(hdl, SA_ZPL_MODE(dev), &mode, sizeof(mode)); + if (rc || !S_ISREG(mode)) { + sa_handle_destroy(hdl); + GOTO(cleanup, rc); + } + + rc = -sa_lookup(hdl, SA_ZPL_LINKS(dev), &nlink, sizeof(nlink)); + if (rc == 0 && nlink > 1) + scrub->os_has_ml_file = 1; + + sa_handle_destroy(hdl); + } + +cleanup: if (nvbuf) nvlist_free(nvbuf); @@ -553,6 +576,9 @@ static int osd_scrub_exec(const struct lu_env *env, struct osd_device *dev, return 0; } +static int osd_scan_ml_file_main(const struct lu_env *env, + struct osd_device *dev); + static int osd_scrub_main(void *args) { struct lu_env env; @@ -560,7 +586,7 @@ static int osd_scrub_main(void *args) struct lustre_scrub *scrub = &dev->od_scrub; struct lu_fid *fid; uint64_t oid; - int rc = 0; + int rc = 0, ret; ENTRY; rc = lu_env_init(&env, LCT_LOCAL | LCT_DT_THREAD); @@ -617,6 +643,12 @@ static int osd_scrub_main(void *args) GOTO(post, rc); post: + if (scrub->os_has_ml_file) { + ret = osd_scan_ml_file_main(&env, dev); + if (ret != 0) + rc = ret; + } + rc = osd_scrub_post(&env, dev, rc); CDEBUG(D_LFSCK, "%s: OI scrub: stop, pos = %llu: rc = %d\n", scrub->os_name, scrub->os_pos_current, rc); @@ -1911,3 +1943,218 @@ int osd_oii_lookup(struct osd_device *dev, const struct lu_fid *fid, RETURN(ret); } + +typedef int (*scan_dir_helper_t)(const struct lu_env *env, + struct osd_device *dev, uint64_t dir_oid, + struct osd_zap_it *ozi); + +static int osd_scan_dir(const struct lu_env *env, struct osd_device *dev, + uint64_t id, scan_dir_helper_t cb) +{ + struct osd_zap_it *it; + struct luz_direntry *zde; + zap_attribute_t *za; + int rc; + + ENTRY; + + OBD_SLAB_ALLOC_PTR_GFP(it, osd_zapit_cachep, GFP_NOFS); + if (it == NULL) + RETURN(-ENOMEM); + + rc = osd_zap_cursor_init(&it->ozi_zc, dev->od_os, id, 0); + if (rc != 0) + GOTO(out, rc); + + za = &it->ozi_za; + zde = &it->ozi_zde; + while (1) { + rc = -zap_cursor_retrieve(it->ozi_zc, za); + if (unlikely(rc)) { + if (rc == -ENOENT) + rc = 0; + + break; + } + + if (name_is_dot_or_dotdot(za->za_name, strlen(za->za_name))) { + zap_cursor_advance(it->ozi_zc); + continue; + } + + strncpy(it->ozi_name, za->za_name, sizeof(it->ozi_name)); + if (za->za_integer_length != 8) { + rc = -EIO; + break; + } + + rc = osd_zap_lookup(dev, it->ozi_zc->zc_zapobj, NULL, + za->za_name, za->za_integer_length, + sizeof(*zde) / za->za_integer_length, zde); + if (rc) + break; + + rc = cb(env, dev, id, it); + if (rc) + break; + + zap_cursor_advance(it->ozi_zc); + } + osd_zap_cursor_fini(it->ozi_zc); + +out: + OBD_SLAB_FREE_PTR(it, osd_zapit_cachep); + RETURN(rc); +} + +static int osd_remove_ml_file(const struct lu_env *env, struct osd_device *dev, + uint64_t dir, uint64_t id, struct lu_fid *fid, + char *name) +{ + struct osd_thread_info *info = osd_oti_get(env); + struct dt_object *dt; + struct osd_object *obj = NULL; + dmu_tx_t *tx; + sa_handle_t *hdl; + uint64_t nlink; + int rc; + + rc = -sa_handle_get(dev->od_os, id, NULL, SA_HDL_PRIVATE, &hdl); + if (rc) + RETURN(rc); + + dt = lu2dt(lu_object_find_slice(env, osd2lu_dev(dev), fid, NULL)); + if (IS_ERR(dt)) + RETURN(PTR_ERR(dt)); + + if (dt) { + obj = osd_dt_obj(dt); + down_read(&obj->oo_guard); + } + + rc = -sa_lookup(hdl, SA_ZPL_LINKS(dev), &nlink, sizeof(nlink)); + if (rc) + GOTO(out, rc); + + if (nlink <= 1) { + CERROR("%s: multi-link file O/%s/%s/%s has nlink %llu\n", + osd_name(dev), info->oti_seq_name, info->oti_dir_name, + name, nlink); + GOTO(out, rc = 0); + } + + tx = dmu_tx_create(dev->od_os); + if (!tx) { + CERROR("%s: fail to create tx to remove multi-link file!\n", + osd_name(dev)); + GOTO(out, rc = -ENOMEM); + } + + dmu_tx_hold_zap(tx, dir, FALSE, NULL); + rc = -dmu_tx_assign(tx, TXG_WAIT); + if (rc) + GOTO(abort, rc); + + nlink--; + rc = -sa_update(hdl, SA_ZPL_LINKS(dev), &nlink, sizeof(nlink), tx); + if (rc) + GOTO(abort, rc); + + rc = -zap_remove(dev->od_os, dir, name, tx); + if (rc) + GOTO(abort, rc); + + dmu_tx_commit(tx); + GOTO(out, rc); + +abort: + dmu_tx_abort(tx); + +out: + if (dt) { + up_read(&obj->oo_guard); + dt_object_put_nocache(env, dt); + } + + sa_handle_destroy(hdl); + RETURN(rc); +} + +static int osd_scan_ml_file(const struct lu_env *env, struct osd_device *dev, + uint64_t dir_oid, struct osd_zap_it *ozi) +{ + struct osd_thread_info *info = osd_oti_get(env); + struct lu_fid *fid = &info->oti_fid; + struct ost_id *ostid = &info->oti_ostid; + char name[32]; + u64 seq; + int rc = 0; + + ENTRY; + + rc = osd_get_fid_by_oid(env, dev, ozi->ozi_zde.lzd_reg.zde_dnode, fid); + if (rc) + RETURN(rc); + + seq = fid_seq(fid); + fid_to_ostid(fid, ostid); + + snprintf(name, sizeof(name), (fid_seq_is_rsvd(seq) || + fid_seq_is_mdt0(seq)) ? "%llu" : "%llx", + fid_seq_is_idif(seq) ? 0 : seq); + if (strcmp(info->oti_seq_name, name) != 0) + GOTO(fix, rc); + + snprintf(name, sizeof(name), "d%d", + (int)ostid_id(ostid) % OSD_OST_MAP_SIZE); + if (strcmp(info->oti_dir_name, name) != 0) + GOTO(fix, rc); + + snprintf(name, sizeof(name), "%llu", ostid_id(ostid)); + if (strcmp(ozi->ozi_name, name) == 0) + RETURN(0); + +fix: + CDEBUG(D_LFSCK, "%s: the file O/%s/%s/%s is corrupted\n", + osd_name(dev), info->oti_seq_name, info->oti_dir_name, + ozi->ozi_name); + + rc = osd_remove_ml_file(env, dev, dir_oid, + ozi->ozi_zde.lzd_reg.zde_dnode, fid, + ozi->ozi_name); + RETURN(rc); +} + +static int osd_scan_ml_file_dir(const struct lu_env *env, + struct osd_device *dev, uint64_t dir_oid, + struct osd_zap_it *ozi) +{ + struct osd_thread_info *info = osd_oti_get(env); + + if (!S_ISDIR(cpu_to_le16(DTTOIF(ozi->ozi_zde.lzd_reg.zde_type)))) + return 0; + + info->oti_dir_name = ozi->ozi_name; + return osd_scan_dir(env, dev, ozi->ozi_zde.lzd_reg.zde_dnode, + osd_scan_ml_file); +} + +static int osd_scan_ml_file_seq(const struct lu_env *env, + struct osd_device *dev, uint64_t dir_oid, + struct osd_zap_it *ozi) +{ + struct osd_thread_info *info = osd_oti_get(env); + + if (!S_ISDIR(cpu_to_le16(DTTOIF(ozi->ozi_zde.lzd_reg.zde_type)))) + return 0; + + info->oti_seq_name = ozi->ozi_name; + return osd_scan_dir(env, dev, ozi->ozi_zde.lzd_reg.zde_dnode, + osd_scan_ml_file_dir); +} + +static int osd_scan_ml_file_main(const struct lu_env *env, + struct osd_device *dev) +{ + return osd_scan_dir(env, dev, dev->od_O_id, osd_scan_ml_file_seq); +} diff --git a/lustre/tests/sanity-scrub.sh b/lustre/tests/sanity-scrub.sh index c253814..80f78b7 100644 --- a/lustre/tests/sanity-scrub.sh +++ b/lustre/tests/sanity-scrub.sh @@ -1338,6 +1338,50 @@ test_18() { } run_test 18 "test mount -o resetoi to recreate OI files" +test_19() { + local rcmd="do_facet ost${ost}" + + check_mount_and_prep + $LFS setstripe -c 1 -i 0 $DIR/$tdir + createmany -o $DIR/$tdir/f 64 || error "(0) Fail to create 32 files." + + echo "stopall" + stopall > /dev/null + + # create mulitple link file + mount_fstype ost1 || error "(1) Fail to mount ost1" + mntpt=$(facet_mntpt ost1) + + local path=$mntpt/O/0/d2 + local file=$(${rcmd} ls $path | awk '{print $0; exit}') + + # create link to the first file + echo "link $path/1 to $path/$file" + ${rcmd} ln $path/$file $path/1 + unmount_fstype ost1 || error "(2) Fail to umount ost1" + + start ost1 $(ostdevname 1) $MOUNT_OPTS_NOSCRUB > /dev/null || + error "(2) Fail to start ost1" + + $START_SCRUB_ON_OST -r || error "(3) Fail to start OI scrub on OST!" + + wait_update_facet ost1 "$LCTL get_param -n \ + osd-*.$(facet_svc ost1).oi_scrub | + awk '/^status/ { print \\\$2 }'" "completed" 6 || + error "(4) Expected '$expected' on ost1" + + stop ost1 + mount_fstype ost1 || error "(5) Fail to mount ost1" + links=$(do_facet ost1 "stat $path/$file" | awk '/Links:/ { print $6 }') + unmount_fstype ost1 || error "(6) Fail to umount ost1" + + start ost1 $(ostdevname 1) $MOUNT_OPTS_NOSCRUB > /dev/null || + error "(7) Fail to start ost1" + + (( links == 1)) || error "(8) object links $links != 1 after scrub" +} +run_test 19 "LFSCK can fix multiple linked files on OST" + # restore MDS/OST size MDSSIZE=${SAVED_MDSSIZE} OSTSIZE=${SAVED_OSTSIZE}