+ if (rc > 0)
+ repaired = true;
+
+ /* Add the missing name entry to the parent. */
+ rc = lfsck_namespace_insert_normal(env, com,
+ parent, child, cname);
+ if (unlikely(rc == -EEXIST))
+ /* Unfortunately, someone reused the
+ * name under the parent by race. So we
+ * have to remove the linkEA entry from
+ * current child object. It means that
+ * the LFSCK cannot recover the system
+ * totally back to its original status,
+ * but it is necessary to make the
+ * current system to be consistent. */
+ rc = lfsck_namespace_shrink_linkea(env,
+ com, child, &ldata,
+ cname, pfid, true);
+ else
+ linkea_next_entry(&ldata);
+ }
+
+ lfsck_object_put(env, parent);
+ if (rc < 0)
+ GOTO(out, rc);
+
+ if (rc > 0)
+ repaired = true;
+
+ continue;
+ } /* !dt_object_exists(parent) */
+
+ /* The linkEA entry with bad parent will be removed. */
+ if (unlikely(!dt_try_as_dir(env, parent))) {
+ lfsck_object_put(env, parent);
+ rc = lfsck_namespace_shrink_linkea(env, com, child,
+ &ldata, cname, pfid, true);
+ if (rc < 0)
+ GOTO(out, rc);
+
+ if (rc > 0)
+ repaired = true;
+
+ continue;
+ }
+
+ rc = dt_lookup(env, parent, (struct dt_rec *)cfid,
+ (const struct dt_key *)cname->ln_name);
+ if (rc != 0 && rc != -ENOENT) {
+ lfsck_object_put(env, parent);
+
+ GOTO(out, rc);
+ }
+
+ if (rc == 0) {
+ if (lu_fid_eq(cfid, lfsck_dto2fid(child))) {
+ /* It is the most common case that we
+ * find the name entry corresponding
+ * to the linkEA entry. */
+ lfsck_object_put(env, parent);
+ linkea_next_entry(&ldata);
+ } else {
+ /* The name entry references another
+ * MDT-object that may be created by
+ * the LFSCK for repairing dangling
+ * name entry. Try to replace it. */
+ rc = lfsck_namespace_replace_cond(env, com,
+ parent, child, cfid, cname);
+ lfsck_object_put(env, parent);
+ if (rc < 0)
+ GOTO(out, rc);
+
+ if (rc > 0) {
+ repaired = true;
+ linkea_next_entry(&ldata);
+ } else {
+ rc = lfsck_namespace_shrink_linkea(env,
+ com, child, &ldata,
+ cname, pfid, true);
+ if (rc < 0)
+ GOTO(out, rc);
+
+ if (rc > 0)
+ repaired = true;
+ }
+ }
+
+ continue;
+ }
+
+ /* The following handles -ENOENT case */
+
+ rc = dt_attr_get(env, child, la);
+ if (rc != 0)
+ GOTO(out, rc);
+
+ /* If there is no name entry in the parent dir and the object
+ * link count is fewer than the linkea entries count, then the
+ * linkea entry should be removed. */
+ if (ldata.ld_leh->leh_reccount > la->la_nlink) {
+ rc = lfsck_namespace_shrink_linkea_cond(env, com,
+ parent, child, &ldata, cname, pfid);
+ lfsck_object_put(env, parent);
+ if (rc < 0)
+ GOTO(out, rc);
+
+ if (rc > 0)
+ repaired = true;
+
+ continue;
+ }
+
+ /* If the LFSCK is marked as LF_INCOMPLETE, then means some
+ * MDT has ever tried to verify some remote MDT-object that
+ * resides on this MDT, but this MDT failed to respond such
+ * request. So means there may be some remote name entry on
+ * other MDT that references this object with another name,
+ * so we cannot know whether this linkEA is valid or not.
+ * So keep it there and maybe resolved when next LFSCK run. */
+ if (ns->ln_flags & LF_INCOMPLETE) {
+ lfsck_object_put(env, parent);
+
+ GOTO(out, rc = 0);
+ }
+
+ rc = lfsck_namespace_check_name(env, lfsck, parent, child,
+ cname);
+ if (rc == -ENOENT)
+ goto lost_parent;
+
+ if (rc < 0) {
+ lfsck_object_put(env, parent);
+
+ GOTO(out, rc);
+ }
+
+ /* It is an invalid name entry, drop it. */
+ if (unlikely(rc > 0)) {
+ lfsck_object_put(env, parent);
+ rc = lfsck_namespace_shrink_linkea(env, com, child,
+ &ldata, cname, pfid, true);
+ if (rc < 0)
+ GOTO(out, rc);
+
+ if (rc > 0)
+ repaired = true;
+
+ continue;
+ }
+
+ /* Add the missing name entry back to the namespace. */
+ rc = lfsck_namespace_insert_normal(env, com, parent, child,
+ cname);
+ if (unlikely(rc == -ESTALE))
+ /* It may happen when the remote object has been
+ * removed, but the local MDT is not aware of that. */
+ goto lost_parent;
+
+ if (unlikely(rc == -EEXIST))
+ /* Unfortunately, someone reused the name under the
+ * parent by race. So we have to remove the linkEA
+ * entry from current child object. It means that the
+ * LFSCK cannot recover the system totally back to
+ * its original status, but it is necessary to make
+ * the current system to be consistent.
+ *
+ * It also may be because of the LFSCK found some
+ * internal status of create operation. Under such
+ * case, nothing to be done. */
+ rc = lfsck_namespace_shrink_linkea_cond(env, com,
+ parent, child, &ldata, cname, pfid);
+ else
+ linkea_next_entry(&ldata);
+
+ lfsck_object_put(env, parent);
+ if (rc < 0)
+ GOTO(out, rc);
+
+ if (rc > 0)
+ repaired = true;
+ }
+
+ GOTO(out, rc = 0);
+
+out:
+ if (rc < 0 && rc != -ENODATA)
+ return rc;
+
+ if (rc == 0 && ldata.ld_leh != NULL)
+ count = ldata.ld_leh->leh_reccount;
+
+ if (count == 0) {
+ /* If the LFSCK is marked as LF_INCOMPLETE, then means some
+ * MDT has ever tried to verify some remote MDT-object that
+ * resides on this MDT, but this MDT failed to respond such
+ * request. So means there may be some remote name entry on
+ * other MDT that references this object with another name,
+ * so we cannot know whether this linkEA is valid or not.
+ * So keep it there and maybe resolved when next LFSCK run. */
+ if (!(ns->ln_flags & LF_INCOMPLETE) &&
+ (ldata.ld_leh == NULL ||
+ !ldata.ld_leh->leh_overflow_time)) {
+ /* If the child becomes orphan, then insert it into
+ * the global .lustre/lost+found/MDTxxxx directory. */
+ rc = lfsck_namespace_insert_orphan(env, com, child,
+ "", "O", &count);
+ if (rc < 0)
+ return rc;
+
+ if (rc > 0) {
+ ns->ln_mul_ref_repaired++;
+ repaired = true;
+ }
+ }
+ } else {
+ rc = dt_attr_get(env, child, la);
+ if (rc != 0)
+ return rc;
+
+ if (la->la_nlink != 0 && la->la_nlink != count) {
+ if (unlikely(!S_ISREG(lfsck_object_type(child)) &&
+ !S_ISLNK(lfsck_object_type(child)))) {
+ CDEBUG(D_LFSCK, "%s: namespace LFSCK finds "
+ "the object "DFID"'s nlink count %d "
+ "does not match linkEA count %d, "
+ "type %o, skip it.\n",
+ lfsck_lfsck2name(lfsck),
+ PFID(lfsck_dto2fid(child)),
+ la->la_nlink, count,
+ lfsck_object_type(child));
+ } else if (la->la_nlink < count &&
+ likely(!ldata.ld_leh->leh_overflow_time)) {
+ rc = lfsck_namespace_repair_nlink(env, com,
+ child, la);
+ if (rc > 0) {
+ ns->ln_objs_nlink_repaired++;
+ rc = 0;
+ }
+ }
+ }
+ }
+
+ if (repaired) {
+ if (la->la_nlink > 1)
+ ns->ln_mul_linked_repaired++;
+
+ if (rc == 0)
+ rc = 1;
+ }
+
+ if (!rc && flags & LNTF_CHECK_AGENT_ENTRY)
+ rc = lfsck_namespace_check_agent_entry(env, com, child);
+
+ return rc;
+}
+
+static void lfsck_namespace_dump_statistics(struct seq_file *m,
+ struct lfsck_namespace *ns,
+ __u64 checked_phase1,
+ __u64 checked_phase2,
+ time64_t time_phase1,
+ time64_t time_phase2, bool dryrun)
+{
+ const char *postfix = dryrun ? "inconsistent" : "repaired";
+
+ seq_printf(m, "checked_phase1: %llu\n"
+ "checked_phase2: %llu\n"
+ "%s_phase1: %llu\n"
+ "%s_phase2: %llu\n"
+ "failed_phase1: %llu\n"
+ "failed_phase2: %llu\n"
+ "directories: %llu\n"
+ "dirent_%s: %llu\n"
+ "linkea_%s: %llu\n"
+ "nlinks_%s: %llu\n"
+ "multiple_linked_checked: %llu\n"
+ "multiple_linked_%s: %llu\n"
+ "unknown_inconsistency: %llu\n"
+ "unmatched_pairs_%s: %llu\n"
+ "dangling_%s: %llu\n"
+ "multiple_referenced_%s: %llu\n"
+ "bad_file_type_%s: %llu\n"
+ "lost_dirent_%s: %llu\n"
+ "local_lost_found_scanned: %llu\n"
+ "local_lost_found_moved: %llu\n"
+ "local_lost_found_skipped: %llu\n"
+ "local_lost_found_failed: %llu\n"
+ "striped_dirs_scanned: %llu\n"
+ "striped_dirs_%s: %llu\n"
+ "striped_dirs_failed: %llu\n"
+ "striped_dirs_disabled: %llu\n"
+ "striped_dirs_skipped: %llu\n"
+ "striped_shards_scanned: %llu\n"
+ "striped_shards_%s: %llu\n"
+ "striped_shards_failed: %llu\n"
+ "striped_shards_skipped: %llu\n"
+ "name_hash_%s: %llu\n"
+ "linkea_overflow_%s: %llu\n"
+ "agent_entries_%s: %llu\n"
+ "success_count: %u\n"
+ "run_time_phase1: %lld seconds\n"
+ "run_time_phase2: %lld seconds\n",
+ checked_phase1,
+ checked_phase2,
+ dryrun ? "inconsistent" : "updated",
+ ns->ln_items_repaired,
+ dryrun ? "inconsistent" : "updated",
+ ns->ln_objs_repaired_phase2,
+ ns->ln_items_failed,
+ ns->ln_objs_failed_phase2,
+ ns->ln_dirs_checked,
+ postfix, ns->ln_dirent_repaired,
+ postfix, ns->ln_linkea_repaired,
+ postfix, ns->ln_objs_nlink_repaired,
+ ns->ln_mul_linked_checked,
+ postfix, ns->ln_mul_linked_repaired,
+ ns->ln_unknown_inconsistency,
+ postfix, ns->ln_unmatched_pairs_repaired,
+ postfix, ns->ln_dangling_repaired,
+ postfix, ns->ln_mul_ref_repaired,
+ postfix, ns->ln_bad_type_repaired,
+ postfix, ns->ln_lost_dirent_repaired,
+ ns->ln_local_lpf_scanned,
+ ns->ln_local_lpf_moved,
+ ns->ln_local_lpf_skipped,
+ ns->ln_local_lpf_failed,
+ ns->ln_striped_dirs_scanned,
+ postfix, ns->ln_striped_dirs_repaired,
+ ns->ln_striped_dirs_failed,
+ ns->ln_striped_dirs_disabled,
+ ns->ln_striped_dirs_skipped,
+ ns->ln_striped_shards_scanned,
+ postfix, ns->ln_striped_shards_repaired,
+ ns->ln_striped_shards_failed,
+ ns->ln_striped_shards_skipped,
+ postfix, ns->ln_name_hash_repaired,
+ dryrun ? "inconsistent" : "cleared",
+ ns->ln_linkea_overflow_cleared,
+ postfix, ns->ln_agent_entries_repaired,
+ ns->ln_success_count,
+ time_phase1,
+ time_phase2);
+}
+
+static void lfsck_namespace_release_lmv(const struct lu_env *env,
+ struct lfsck_component *com)
+{
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct lfsck_namespace *ns = com->lc_file_ram;
+
+ while (!list_empty(&lfsck->li_list_lmv)) {
+ struct lfsck_lmv_unit *llu;
+ struct lfsck_lmv *llmv;
+
+ llu = list_entry(lfsck->li_list_lmv.next,
+ struct lfsck_lmv_unit, llu_link);
+ llmv = &llu->llu_lmv;
+
+ LASSERTF(atomic_read(&llmv->ll_ref) == 1,
+ "still in using: %u\n",
+ atomic_read(&llmv->ll_ref));
+
+ ns->ln_striped_dirs_skipped++;
+ lfsck_lmv_put(env, llmv);
+ }
+}
+
+static int lfsck_namespace_check_for_double_scan(const struct lu_env *env,
+ struct lfsck_component *com,
+ struct dt_object *obj)
+{
+ struct lu_attr *la = &lfsck_env_info(env)->lti_la;
+ int rc;
+
+ rc = dt_attr_get(env, obj, la);
+ if (rc != 0)
+ return rc;
+
+ /* zero-linkEA object may be orphan, but it also maybe because
+ * of upgrading. Currently, we cannot record it for double scan.
+ * Because it may cause the LFSCK trace file to be too large. */
+
+ /* "la_ctime" == 1 means that it has ever been removed from
+ * backend /lost+found directory but not been added back to
+ * the normal namespace yet. */
+
+ if ((S_ISREG(lfsck_object_type(obj)) && la->la_nlink > 1) ||
+ unlikely(la->la_ctime == 1))
+ rc = lfsck_namespace_trace_update(env, com, lfsck_dto2fid(obj),
+ LNTF_CHECK_LINKEA, true);
+
+ return rc;
+}
+
+/* namespace APIs */
+
+static int lfsck_namespace_reset(const struct lu_env *env,
+ struct lfsck_component *com, bool init)
+{
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ struct lfsck_assistant_data *lad = com->lc_data;
+ struct dt_object *root;
+ int rc;
+ ENTRY;
+
+ root = dt_locate(env, lfsck->li_bottom, &lfsck->li_local_root_fid);
+ if (IS_ERR(root))
+ GOTO(log, rc = PTR_ERR(root));
+
+ if (unlikely(!dt_try_as_dir(env, root)))
+ GOTO(put, rc = -ENOTDIR);
+
+ down_write(&com->lc_sem);
+ if (init) {
+ memset(ns, 0, sizeof(*ns));
+ } else {
+ __u32 count = ns->ln_success_count;
+ time64_t last_time = ns->ln_time_last_complete;
+
+ memset(ns, 0, sizeof(*ns));
+ ns->ln_success_count = count;
+ ns->ln_time_last_complete = last_time;
+ }
+ ns->ln_magic = LFSCK_NAMESPACE_MAGIC;
+ ns->ln_status = LS_INIT;
+ ns->ln_time_latest_reset = ktime_get_real_seconds();
+
+ rc = lfsck_load_one_trace_file(env, com, root, &com->lc_obj,
+ &dt_lfsck_namespace_features,
+ LFSCK_NAMESPACE, true);
+ if (rc)
+ GOTO(out, rc);
+
+ rc = lfsck_load_sub_trace_files(env, com, &dt_lfsck_namespace_features,
+ LFSCK_NAMESPACE, true);
+ if (rc != 0)
+ GOTO(out, rc);
+
+ clear_bit(LAD_INCOMPLETE, &lad->lad_flags);
+ CFS_RESET_BITMAP(lad->lad_bitmap);
+
+ rc = lfsck_namespace_store(env, com);
+
+ GOTO(out, rc);
+
+out:
+ up_write(&com->lc_sem);
+
+put:
+ lfsck_object_put(env, root);
+log:
+ CDEBUG(D_LFSCK, "%s: namespace LFSCK reset: rc = %d\n",
+ lfsck_lfsck2name(lfsck), rc);
+ return rc;
+}
+
+static void
+lfsck_namespace_fail(const struct lu_env *env, struct lfsck_component *com,
+ bool new_checked)
+{
+ struct lfsck_namespace *ns = com->lc_file_ram;
+
+ down_write(&com->lc_sem);
+ if (new_checked)
+ com->lc_new_checked++;
+ lfsck_namespace_record_failure(env, com->lc_lfsck, ns);
+ up_write(&com->lc_sem);
+}
+
+static void lfsck_namespace_close_dir(const struct lu_env *env,
+ struct lfsck_component *com)
+{
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ struct lfsck_assistant_data *lad = com->lc_data;
+ struct lfsck_assistant_object *lso = NULL;
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct lfsck_lmv *llmv = lfsck->li_lmv;
+ struct lfsck_namespace_req *lnr;
+ struct lu_attr *la = &lfsck_env_info(env)->lti_la2;
+ __u32 size = sizeof(*lnr) + LFSCK_TMPBUF_LEN;
+ int rc;
+ bool wakeup = false;
+ ENTRY;
+
+ if (llmv == NULL)
+ RETURN_EXIT;
+
+ rc = dt_attr_get(env, lfsck->li_obj_dir, la);
+ if (rc)
+ RETURN_EXIT;
+
+ OBD_ALLOC(lnr, size);
+ if (lnr == NULL) {
+ ns->ln_striped_dirs_skipped++;
+
+ RETURN_EXIT;
+ }
+
+ lso = lfsck_assistant_object_init(env, lfsck_dto2fid(lfsck->li_obj_dir),
+ la, lfsck->li_pos_current.lp_oit_cookie, true);
+ if (IS_ERR(lso)) {
+ OBD_FREE(lnr, size);
+ ns->ln_striped_dirs_skipped++;
+
+ RETURN_EXIT;
+ }
+
+ /* Generate a dummy request to indicate that all shards' name entry
+ * in this striped directory has been scanned for the first time. */
+ INIT_LIST_HEAD(&lnr->lnr_lar.lar_list);
+ lnr->lnr_lar.lar_parent = lso;
+ lnr->lnr_lmv = lfsck_lmv_get(llmv);
+ lnr->lnr_fid = *lfsck_dto2fid(lfsck->li_obj_dir);
+ lnr->lnr_dir_cookie = MDS_DIR_END_OFF;
+ lnr->lnr_size = size;
+ lnr->lnr_type = lso->lso_attr.la_mode;
+
+ spin_lock(&lad->lad_lock);
+ if (lad->lad_assistant_status < 0 ||
+ unlikely(!thread_is_running(&lfsck->li_thread) ||
+ !thread_is_running(&lad->lad_thread))) {
+ spin_unlock(&lad->lad_lock);
+ lfsck_namespace_assistant_req_fini(env, &lnr->lnr_lar);
+ ns->ln_striped_dirs_skipped++;
+
+ RETURN_EXIT;
+ }
+
+ list_add_tail(&lnr->lnr_lar.lar_list, &lad->lad_req_list);
+ if (lad->lad_prefetched == 0)
+ wakeup = true;
+
+ lad->lad_prefetched++;
+ spin_unlock(&lad->lad_lock);
+ if (wakeup)
+ wake_up_all(&lad->lad_thread.t_ctl_waitq);
+
+ EXIT;
+}
+
+static int lfsck_namespace_open_dir(const struct lu_env *env,
+ struct lfsck_component *com)
+{
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ struct lfsck_lmv *llmv = lfsck->li_lmv;
+ int rc = 0;
+ ENTRY;
+
+ if (llmv == NULL)
+ RETURN(0);
+
+ if (llmv->ll_lmv_master) {
+ struct lmv_mds_md_v1 *lmv = &llmv->ll_lmv;
+
+ if (lmv->lmv_master_mdt_index != lfsck_dev_idx(lfsck)) {
+ lmv->lmv_master_mdt_index =
+ lfsck_dev_idx(lfsck);
+ ns->ln_flags |= LF_INCONSISTENT;
+ llmv->ll_lmv_updated = 1;
+ }
+ } else {
+ rc = lfsck_namespace_verify_stripe_slave(env, com,
+ lfsck->li_obj_dir, llmv);
+ }
+
+ RETURN(rc > 0 ? 0 : rc);
+}
+
+static int lfsck_namespace_checkpoint(const struct lu_env *env,
+ struct lfsck_component *com, bool init)
+{
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ int rc;
+
+ if (!init) {
+ rc = lfsck_checkpoint_generic(env, com);
+ if (rc != 0)
+ goto log;
+ }
+
+ down_write(&com->lc_sem);
+ if (init) {
+ ns->ln_pos_latest_start = lfsck->li_pos_checkpoint;
+ } else {
+ ns->ln_pos_last_checkpoint = lfsck->li_pos_checkpoint;
+ ns->ln_run_time_phase1 += ktime_get_seconds() -
+ lfsck->li_time_last_checkpoint;
+ ns->ln_time_last_checkpoint = ktime_get_real_seconds();
+ ns->ln_items_checked += com->lc_new_checked;
+ com->lc_new_checked = 0;
+ }
+
+ rc = lfsck_namespace_store(env, com);
+ up_write(&com->lc_sem);
+
+log:
+ CDEBUG(D_LFSCK, "%s: namespace LFSCK checkpoint at the pos [%llu"
+ ", "DFID", %#llx], status = %d: rc = %d\n",
+ lfsck_lfsck2name(lfsck), lfsck->li_pos_current.lp_oit_cookie,
+ PFID(&lfsck->li_pos_current.lp_dir_parent),
+ lfsck->li_pos_current.lp_dir_cookie, ns->ln_status, rc);
+
+ return rc > 0 ? 0 : rc;
+}
+
+static int lfsck_namespace_prep(const struct lu_env *env,
+ struct lfsck_component *com,
+ struct lfsck_start_param *lsp)
+{
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ struct lfsck_position *pos = &com->lc_pos_start;
+ int rc;
+
+ rc = lfsck_namespace_load_bitmap(env, com);
+ if (rc != 0 || ns->ln_status == LS_COMPLETED) {
+ rc = lfsck_namespace_reset(env, com, false);
+ if (rc == 0)
+ rc = lfsck_set_param(env, lfsck, lsp->lsp_start, true);
+
+ if (rc != 0) {
+ CDEBUG(D_LFSCK, "%s: namespace LFSCK prep failed: "
+ "rc = %d\n", lfsck_lfsck2name(lfsck), rc);
+
+ return rc;
+ }
+ }
+
+ down_write(&com->lc_sem);
+ ns->ln_time_latest_start = ktime_get_real_seconds();
+ spin_lock(&lfsck->li_lock);
+
+ if (ns->ln_flags & LF_SCANNED_ONCE) {
+ if (!lfsck->li_drop_dryrun ||
+ lfsck_pos_is_zero(&ns->ln_pos_first_inconsistent)) {
+ ns->ln_status = LS_SCANNING_PHASE2;
+ list_move_tail(&com->lc_link,
+ &lfsck->li_list_double_scan);
+ if (!list_empty(&com->lc_link_dir))
+ list_del_init(&com->lc_link_dir);
+ lfsck_pos_set_zero(pos);
+ } else {
+ ns->ln_status = LS_SCANNING_PHASE1;
+ ns->ln_run_time_phase1 = 0;
+ ns->ln_run_time_phase2 = 0;
+ ns->ln_items_checked = 0;
+ ns->ln_items_repaired = 0;
+ ns->ln_items_failed = 0;
+ ns->ln_dirs_checked = 0;
+ ns->ln_objs_checked_phase2 = 0;
+ ns->ln_objs_repaired_phase2 = 0;
+ ns->ln_objs_failed_phase2 = 0;
+ ns->ln_objs_nlink_repaired = 0;
+ ns->ln_dirent_repaired = 0;
+ ns->ln_linkea_repaired = 0;
+ ns->ln_mul_linked_checked = 0;
+ ns->ln_mul_linked_repaired = 0;
+ ns->ln_unknown_inconsistency = 0;
+ ns->ln_unmatched_pairs_repaired = 0;
+ ns->ln_dangling_repaired = 0;
+ ns->ln_mul_ref_repaired = 0;
+ ns->ln_bad_type_repaired = 0;
+ ns->ln_lost_dirent_repaired = 0;
+ ns->ln_striped_dirs_scanned = 0;
+ ns->ln_striped_dirs_repaired = 0;
+ ns->ln_striped_dirs_failed = 0;
+ ns->ln_striped_dirs_disabled = 0;
+ ns->ln_striped_dirs_skipped = 0;
+ ns->ln_striped_shards_scanned = 0;
+ ns->ln_striped_shards_repaired = 0;
+ ns->ln_striped_shards_failed = 0;
+ ns->ln_striped_shards_skipped = 0;
+ ns->ln_name_hash_repaired = 0;
+ fid_zero(&ns->ln_fid_latest_scanned_phase2);
+ if (list_empty(&com->lc_link_dir))
+ list_add_tail(&com->lc_link_dir,
+ &lfsck->li_list_dir);
+ *pos = ns->ln_pos_first_inconsistent;
+ }
+ } else {
+ ns->ln_status = LS_SCANNING_PHASE1;
+ if (list_empty(&com->lc_link_dir))
+ list_add_tail(&com->lc_link_dir,
+ &lfsck->li_list_dir);
+ if (!lfsck->li_drop_dryrun ||
+ lfsck_pos_is_zero(&ns->ln_pos_first_inconsistent)) {
+ *pos = ns->ln_pos_last_checkpoint;
+ pos->lp_oit_cookie++;
+ } else {
+ *pos = ns->ln_pos_first_inconsistent;
+ }
+ }
+
+ spin_unlock(&lfsck->li_lock);
+ up_write(&com->lc_sem);
+
+ rc = lfsck_start_assistant(env, com, lsp);
+
+ CDEBUG(D_LFSCK, "%s: namespace LFSCK prep done, start pos [%llu, "
+ DFID", %#llx]: rc = %d\n",
+ lfsck_lfsck2name(lfsck), pos->lp_oit_cookie,
+ PFID(&pos->lp_dir_parent), pos->lp_dir_cookie, rc);
+
+ return rc;
+}
+
+static int lfsck_namespace_exec_oit(const struct lu_env *env,
+ struct lfsck_component *com,
+ struct dt_object *obj)
+{
+ struct lfsck_thread_info *info = lfsck_env_info(env);
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ const struct lu_fid *fid = lfsck_dto2fid(obj);
+ struct lu_fid *pfid = &info->lti_fid2;
+ struct lu_name *cname = &info->lti_name;
+ struct lu_seq_range *range = &info->lti_range;
+ struct seq_server_site *ss = lfsck_dev_site(lfsck);
+ struct linkea_data ldata = { NULL };
+ __u32 idx = lfsck_dev_idx(lfsck);
+ struct lu_attr la = { .la_valid = 0 };
+ bool remote = false;
+ int rc;
+ ENTRY;
+
+ rc = dt_attr_get(env, obj, &la);
+ if (unlikely(rc || (la.la_valid & LA_FLAGS &&
+ la.la_flags & LUSTRE_ORPHAN_FL))) {
+ CDEBUG(D_INFO,
+ "%s: skip orphan "DFID", %llx/%x: rc = %d\n",
+ lfsck_lfsck2name(lfsck), PFID(fid),
+ la.la_valid, la.la_flags, rc);
+
+ return rc;
+ }
+
+ rc = lfsck_links_read(env, obj, &ldata);
+ if (rc == -ENOENT)
+ GOTO(out, rc = 0);
+
+ /* -EINVAL means crashed linkEA, should be verified. */
+ if (rc == -EINVAL) {
+ rc = lfsck_namespace_trace_update(env, com, fid,
+ LNTF_CHECK_LINKEA, true);
+ if (rc == 0) {
+ struct lustre_handle lh = { 0 };
+
+ rc = lfsck_ibits_lock(env, lfsck, obj, &lh,
+ MDS_INODELOCK_UPDATE |
+ MDS_INODELOCK_XATTR, LCK_EX);
+ if (rc == 0) {
+ rc = lfsck_namespace_links_remove(env, com,
+ obj);
+ lfsck_ibits_unlock(&lh, LCK_EX);
+ }
+ }
+
+ GOTO(out, rc = (rc == -ENOENT ? 0 : rc));
+ }
+
+ if (rc && rc != -ENODATA)
+ GOTO(out, rc);
+
+ if (rc == -ENODATA || unlikely(!ldata.ld_leh->leh_reccount)) {
+ rc = lfsck_namespace_check_for_double_scan(env, com, obj);
+
+ GOTO(out, rc);
+ }
+
+ linkea_first_entry(&ldata);
+ while (ldata.ld_lee != NULL) {
+ linkea_entry_unpack(ldata.ld_lee, &ldata.ld_reclen,
+ cname, pfid);
+ if (!fid_is_sane(pfid)) {
+ rc = lfsck_namespace_trace_update(env, com, fid,
+ LNTF_CHECK_PARENT, true);
+ } else if (!linkea_entry_is_valid(&ldata, cname, pfid)) {
+ GOTO(out, rc);
+ } else {
+ fld_range_set_mdt(range);
+ rc = fld_server_lookup(env, ss->ss_server_fld,
+ fid_seq(pfid), range);
+ if ((rc == -ENOENT) ||
+ (!rc && range->lsr_index != idx)) {
+ remote = true;
+ break;
+ }
+ }
+ if (rc)
+ GOTO(out, rc);
+
+ linkea_next_entry(&ldata);
+ }
+
+ if ((lu_object_has_agent_entry(&obj->do_lu) && !remote) ||
+ (!lu_object_has_agent_entry(&obj->do_lu) && remote)) {
+ rc = lfsck_namespace_trace_update(env, com, fid,
+ LNTF_CHECK_AGENT_ENTRY, true);
+ if (rc)
+ GOTO(out, rc);
+ }
+
+ /* Record multiple-linked object. */
+ if (ldata.ld_leh->leh_reccount > 1) {
+ rc = lfsck_namespace_trace_update(env, com, fid,
+ LNTF_CHECK_LINKEA, true);
+
+ GOTO(out, rc);
+ }
+
+ if (remote)
+ rc = lfsck_namespace_trace_update(env, com, fid,
+ LNTF_CHECK_LINKEA, true);
+ else
+ rc = lfsck_namespace_check_for_double_scan(env, com, obj);
+
+ GOTO(out, rc);
+
+out:
+ down_write(&com->lc_sem);
+ if (S_ISDIR(lfsck_object_type(obj)))
+ ns->ln_dirs_checked++;
+ if (rc != 0)
+ lfsck_namespace_record_failure(env, com->lc_lfsck, ns);
+ up_write(&com->lc_sem);
+
+ return rc;
+}
+
+static int lfsck_namespace_exec_dir(const struct lu_env *env,
+ struct lfsck_component *com,
+ struct lfsck_assistant_object *lso,
+ struct lu_dirent *ent, __u16 type)
+{
+ struct lfsck_assistant_data *lad = com->lc_data;
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct lfsck_namespace_req *lnr;
+ struct lfsck_bookmark *bk = &lfsck->li_bookmark_ram;
+ struct ptlrpc_thread *mthread = &lfsck->li_thread;
+ struct ptlrpc_thread *athread = &lad->lad_thread;
+ struct l_wait_info lwi = { 0 };
+ bool wakeup = false;
+
+ l_wait_event(mthread->t_ctl_waitq,
+ lad->lad_prefetched < bk->lb_async_windows ||
+ !thread_is_running(mthread) ||
+ !thread_is_running(athread),
+ &lwi);
+
+ if (unlikely(!thread_is_running(mthread) ||
+ !thread_is_running(athread)))
+ return 0;
+
+ if (unlikely(lfsck_is_dead_obj(lfsck->li_obj_dir)))
+ return 0;
+
+ lnr = lfsck_namespace_assistant_req_init(com->lc_lfsck, lso, ent, type);
+ if (IS_ERR(lnr)) {
+ struct lfsck_namespace *ns = com->lc_file_ram;
+
+ lfsck_namespace_record_failure(env, com->lc_lfsck, ns);
+ return PTR_ERR(lnr);
+ }
+
+ spin_lock(&lad->lad_lock);
+ if (lad->lad_assistant_status < 0 ||
+ unlikely(!thread_is_running(mthread) ||
+ !thread_is_running(athread))) {
+ spin_unlock(&lad->lad_lock);
+ lfsck_namespace_assistant_req_fini(env, &lnr->lnr_lar);
+ return lad->lad_assistant_status;
+ }
+
+ list_add_tail(&lnr->lnr_lar.lar_list, &lad->lad_req_list);
+ if (lad->lad_prefetched == 0)
+ wakeup = true;
+
+ lad->lad_prefetched++;
+ spin_unlock(&lad->lad_lock);
+ if (wakeup)
+ wake_up_all(&lad->lad_thread.t_ctl_waitq);
+
+ down_write(&com->lc_sem);
+ com->lc_new_checked++;
+ up_write(&com->lc_sem);
+
+ return 0;
+}
+
+static int lfsck_namespace_post(const struct lu_env *env,
+ struct lfsck_component *com,
+ int result, bool init)
+{
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ int rc;
+ ENTRY;
+
+ lfsck_post_generic(env, com, &result);
+
+ down_write(&com->lc_sem);
+ lfsck_namespace_release_lmv(env, com);
+
+ spin_lock(&lfsck->li_lock);
+ if (!init)
+ ns->ln_pos_last_checkpoint = lfsck->li_pos_checkpoint;
+ if (result > 0) {
+ ns->ln_status = LS_SCANNING_PHASE2;
+ ns->ln_flags |= LF_SCANNED_ONCE;
+ ns->ln_flags &= ~LF_UPGRADE;
+ list_del_init(&com->lc_link_dir);
+ list_move_tail(&com->lc_link, &lfsck->li_list_double_scan);
+ } else if (result == 0) {
+ if (lfsck->li_status != 0)
+ ns->ln_status = lfsck->li_status;
+ else
+ ns->ln_status = LS_STOPPED;
+ if (ns->ln_status != LS_PAUSED) {
+ list_del_init(&com->lc_link_dir);
+ list_move_tail(&com->lc_link, &lfsck->li_list_idle);
+ }
+ } else {
+ ns->ln_status = LS_FAILED;
+ list_del_init(&com->lc_link_dir);
+ list_move_tail(&com->lc_link, &lfsck->li_list_idle);
+ }
+ spin_unlock(&lfsck->li_lock);
+
+ if (!init) {
+ ns->ln_run_time_phase1 += ktime_get_seconds() -
+ lfsck->li_time_last_checkpoint;
+ ns->ln_time_last_checkpoint = ktime_get_real_seconds();
+ ns->ln_items_checked += com->lc_new_checked;
+ com->lc_new_checked = 0;
+ }
+
+ rc = lfsck_namespace_store(env, com);
+ up_write(&com->lc_sem);
+
+ CDEBUG(D_LFSCK, "%s: namespace LFSCK post done: rc = %d\n",
+ lfsck_lfsck2name(lfsck), rc);
+
+ RETURN(rc);
+}
+
+static void
+lfsck_namespace_dump(const struct lu_env *env, struct lfsck_component *com,
+ struct seq_file *m)
+{
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct lfsck_bookmark *bk = &lfsck->li_bookmark_ram;
+ struct lfsck_namespace *ns = com->lc_file_ram;
+
+ down_read(&com->lc_sem);
+ seq_printf(m, "name: lfsck_namespace\n"
+ "magic: %#x\n"
+ "version: %d\n"
+ "status: %s\n",
+ ns->ln_magic,
+ bk->lb_version,
+ lfsck_status2name(ns->ln_status));
+
+ lfsck_bits_dump(m, ns->ln_flags, lfsck_flags_names, "flags");
+
+ lfsck_bits_dump(m, bk->lb_param, lfsck_param_names, "param");
+
+ lfsck_time_dump(m, ns->ln_time_last_complete, "last_completed");
+
+ lfsck_time_dump(m, ns->ln_time_latest_start, "latest_start");
+
+ lfsck_time_dump(m, ns->ln_time_last_checkpoint, "last_checkpoint");
+
+ lfsck_pos_dump(m, &ns->ln_pos_latest_start, "latest_start_position");
+
+ lfsck_pos_dump(m, &ns->ln_pos_last_checkpoint,
+ "last_checkpoint_position");
+
+ lfsck_pos_dump(m, &ns->ln_pos_first_inconsistent,
+ "first_failure_position");
+
+ if (ns->ln_status == LS_SCANNING_PHASE1) {
+ struct lfsck_position pos;
+ time64_t duration = ktime_get_seconds() -
+ lfsck->li_time_last_checkpoint;
+ u64 checked = ns->ln_items_checked + com->lc_new_checked;
+ u64 speed = checked;
+ u64 new_checked = com->lc_new_checked;
+ time64_t rtime = ns->ln_run_time_phase1 + duration;
+
+ if (duration != 0)
+ new_checked = div64_s64(new_checked, duration);
+
+ if (rtime != 0)
+ speed = div64_s64(speed, rtime);
+
+ lfsck_namespace_dump_statistics(m, ns, checked, 0, rtime, 0,
+ bk->lb_param & LPF_DRYRUN);
+ seq_printf(m, "average_speed_phase1: %llu items/sec\n"
+ "average_speed_phase2: N/A\n"
+ "average_speed_total: %llu items/sec\n"
+ "real_time_speed_phase1: %llu items/sec\n"
+ "real_time_speed_phase2: N/A\n",
+ speed,
+ speed,
+ new_checked);
+
+ if (likely(lfsck->li_di_oit)) {
+ const struct dt_it_ops *iops =
+ &lfsck->li_obj_oit->do_index_ops->dio_it;
+
+ /* The low layer otable-based iteration position may NOT
+ * exactly match the namespace-based directory traversal
+ * cookie. Generally, it is not a serious issue. But the
+ * caller should NOT make assumption on that. */
+ pos.lp_oit_cookie = iops->store(env, lfsck->li_di_oit);
+ if (!lfsck->li_current_oit_processed)
+ pos.lp_oit_cookie--;
+
+ spin_lock(&lfsck->li_lock);
+ if (lfsck->li_di_dir) {
+ pos.lp_dir_cookie = lfsck->li_cookie_dir;
+ if (pos.lp_dir_cookie >= MDS_DIR_END_OFF) {
+ fid_zero(&pos.lp_dir_parent);
+ pos.lp_dir_cookie = 0;
+ } else {
+ pos.lp_dir_parent =
+ *lfsck_dto2fid(lfsck->li_obj_dir);
+ }
+ } else {
+ fid_zero(&pos.lp_dir_parent);
+ pos.lp_dir_cookie = 0;
+ }
+ spin_unlock(&lfsck->li_lock);
+ } else {
+ pos = ns->ln_pos_last_checkpoint;
+ }
+
+ lfsck_pos_dump(m, &pos, "current_position");
+ } else if (ns->ln_status == LS_SCANNING_PHASE2) {
+ time64_t duration = ktime_get_seconds() -
+ com->lc_time_last_checkpoint;
+ __u64 checked = ns->ln_objs_checked_phase2 +
+ com->lc_new_checked;
+ __u64 speed1 = ns->ln_items_checked;
+ __u64 speed2 = checked;
+ __u64 speed0 = speed1 + speed2;
+ __u64 new_checked = com->lc_new_checked;
+ time64_t rtime = ns->ln_run_time_phase2 + duration;
+ time64_t time0 = ns->ln_run_time_phase1 + rtime;
+
+ if (duration != 0)
+ new_checked = div64_s64(new_checked, duration);
+
+ if (ns->ln_run_time_phase1 != 0)
+ speed1 = div64_s64(speed1, ns->ln_run_time_phase1);
+ else if (ns->ln_items_checked != 0)
+ time0++;
+
+ if (rtime != 0)
+ speed2 = div64_s64(speed2, rtime);
+ else if (checked != 0)
+ time0++;
+
+ if (time0 != 0)
+ speed0 = div64_s64(speed0, time0);
+
+ lfsck_namespace_dump_statistics(m, ns, ns->ln_items_checked,
+ checked,
+ ns->ln_run_time_phase1, rtime,
+ bk->lb_param & LPF_DRYRUN);
+ seq_printf(m, "average_speed_phase1: %llu items/sec\n"
+ "average_speed_phase2: %llu objs/sec\n"
+ "average_speed_total: %llu items/sec\n"
+ "real_time_speed_phase1: N/A\n"
+ "real_time_speed_phase2: %llu objs/sec\n"
+ "current_position: "DFID"\n",
+ speed1,
+ speed2,
+ speed0,
+ new_checked,
+ PFID(&ns->ln_fid_latest_scanned_phase2));
+ } else {
+ __u64 speed1 = ns->ln_items_checked;
+ __u64 speed2 = ns->ln_objs_checked_phase2;
+ __u64 speed0 = speed1 + speed2;
+ time64_t time0 = ns->ln_run_time_phase1 + ns->ln_run_time_phase2;
+
+ if (ns->ln_run_time_phase1 != 0)
+ speed1 = div64_s64(speed1, ns->ln_run_time_phase1);
+ else if (ns->ln_items_checked != 0)
+ time0++;
+
+ if (ns->ln_run_time_phase2 != 0)
+ speed2 = div64_s64(speed2, ns->ln_run_time_phase2);
+ else if (ns->ln_objs_checked_phase2 != 0)
+ time0++;
+
+ if (time0 != 0)
+ speed0 = div64_s64(speed0, time0);
+
+ lfsck_namespace_dump_statistics(m, ns, ns->ln_items_checked,
+ ns->ln_objs_checked_phase2,
+ ns->ln_run_time_phase1,
+ ns->ln_run_time_phase2,
+ bk->lb_param & LPF_DRYRUN);
+ seq_printf(m, "average_speed_phase1: %llu items/sec\n"
+ "average_speed_phase2: %llu objs/sec\n"
+ "average_speed_total: %llu items/sec\n"
+ "real_time_speed_phase1: N/A\n"
+ "real_time_speed_phase2: N/A\n"
+ "current_position: N/A\n",
+ speed1,
+ speed2,
+ speed0);
+ }
+
+ up_read(&com->lc_sem);
+}
+
+static int lfsck_namespace_double_scan(const struct lu_env *env,
+ struct lfsck_component *com)
+{
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ struct lfsck_assistant_data *lad = com->lc_data;
+ struct lfsck_tgt_descs *ltds = &com->lc_lfsck->li_mdt_descs;
+ struct lfsck_tgt_desc *ltd;
+ struct lfsck_tgt_desc *next;
+ int rc;
+
+ rc = lfsck_double_scan_generic(env, com, ns->ln_status);
+ if (thread_is_stopped(&lad->lad_thread)) {
+ LASSERT(list_empty(&lad->lad_req_list));
+ LASSERT(list_empty(&lad->lad_mdt_phase1_list));
+
+ spin_lock(<ds->ltd_lock);
+ list_for_each_entry_safe(ltd, next, &lad->lad_mdt_phase2_list,
+ ltd_namespace_phase_list) {
+ list_del_init(<d->ltd_namespace_phase_list);
+ }
+ spin_unlock(<ds->ltd_lock);
+ }
+
+ return rc;
+}
+
+static void lfsck_namespace_data_release(const struct lu_env *env,
+ struct lfsck_component *com)
+{
+ struct lfsck_assistant_data *lad = com->lc_data;
+ struct lfsck_tgt_descs *ltds = &com->lc_lfsck->li_mdt_descs;
+ struct lfsck_tgt_desc *ltd;
+ struct lfsck_tgt_desc *next;
+
+ LASSERT(lad != NULL);
+ LASSERT(thread_is_init(&lad->lad_thread) ||
+ thread_is_stopped(&lad->lad_thread));
+ LASSERT(list_empty(&lad->lad_req_list));
+
+ com->lc_data = NULL;
+ lfsck_namespace_release_lmv(env, com);
+
+ spin_lock(<ds->ltd_lock);
+ list_for_each_entry_safe(ltd, next, &lad->lad_mdt_phase1_list,
+ ltd_namespace_phase_list) {
+ list_del_init(<d->ltd_namespace_phase_list);
+ }
+ list_for_each_entry_safe(ltd, next, &lad->lad_mdt_phase2_list,
+ ltd_namespace_phase_list) {
+ list_del_init(<d->ltd_namespace_phase_list);
+ }
+ list_for_each_entry_safe(ltd, next, &lad->lad_mdt_list,
+ ltd_namespace_list) {
+ list_del_init(<d->ltd_namespace_list);
+ }
+ spin_unlock(<ds->ltd_lock);
+
+ if (likely(lad->lad_bitmap != NULL))
+ CFS_FREE_BITMAP(lad->lad_bitmap);
+
+ OBD_FREE_PTR(lad);
+}
+
+static void lfsck_namespace_quit(const struct lu_env *env,
+ struct lfsck_component *com)
+{
+ struct lfsck_assistant_data *lad = com->lc_data;
+ struct lfsck_tgt_descs *ltds = &com->lc_lfsck->li_mdt_descs;
+ struct lfsck_tgt_desc *ltd;
+ struct lfsck_tgt_desc *next;
+
+ LASSERT(lad != NULL);
+
+ lfsck_quit_generic(env, com);
+
+ LASSERT(thread_is_init(&lad->lad_thread) ||
+ thread_is_stopped(&lad->lad_thread));
+ LASSERT(list_empty(&lad->lad_req_list));
+
+ lfsck_namespace_release_lmv(env, com);
+
+ spin_lock(<ds->ltd_lock);
+ list_for_each_entry_safe(ltd, next, &lad->lad_mdt_phase1_list,
+ ltd_namespace_phase_list) {
+ list_del_init(<d->ltd_namespace_phase_list);
+ }
+ list_for_each_entry_safe(ltd, next, &lad->lad_mdt_phase2_list,
+ ltd_namespace_phase_list) {
+ list_del_init(<d->ltd_namespace_phase_list);
+ }
+ spin_unlock(<ds->ltd_lock);
+}
+
+static int lfsck_namespace_in_notify(const struct lu_env *env,
+ struct lfsck_component *com,
+ struct lfsck_request *lr)
+{
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ struct lfsck_assistant_data *lad = com->lc_data;
+ struct lfsck_tgt_descs *ltds = &lfsck->li_mdt_descs;
+ struct lfsck_tgt_desc *ltd;
+ int rc = 0;
+ bool fail = false;
+ ENTRY;
+
+ switch (lr->lr_event) {
+ case LE_SET_LMV_MASTER: {
+ struct dt_object *obj;
+
+ obj = lfsck_object_find_bottom(env, lfsck, &lr->lr_fid);
+ if (IS_ERR(obj))
+ RETURN(PTR_ERR(obj));
+
+ if (likely(dt_object_exists(obj)))
+ rc = lfsck_namespace_notify_lmv_master_local(env, com,
+ obj);
+
+ lfsck_object_put(env, obj);
+
+ RETURN(rc > 0 ? 0 : rc);
+ }
+ case LE_SET_LMV_SLAVE: {
+ if (!(lr->lr_flags & LEF_RECHECK_NAME_HASH))
+ ns->ln_striped_shards_repaired++;
+
+ rc = lfsck_namespace_trace_update(env, com, &lr->lr_fid,
+ LNTF_RECHECK_NAME_HASH, true);
+
+ RETURN(rc > 0 ? 0 : rc);
+ }
+ case LE_PHASE1_DONE:
+ case LE_PHASE2_DONE:
+ case LE_PEER_EXIT:
+ break;
+ default:
+ RETURN(-EINVAL);
+ }
+
+ CDEBUG(D_LFSCK, "%s: namespace LFSCK handles notify %u from MDT %x, "
+ "status %d, flags %x\n", lfsck_lfsck2name(lfsck), lr->lr_event,
+ lr->lr_index, lr->lr_status, lr->lr_flags2);
+
+ spin_lock(<ds->ltd_lock);
+ ltd = lfsck_ltd2tgt(ltds, lr->lr_index);
+ if (ltd == NULL) {
+ spin_unlock(<ds->ltd_lock);
+
+ RETURN(-ENXIO);
+ }
+
+ list_del_init(<d->ltd_namespace_phase_list);
+ switch (lr->lr_event) {
+ case LE_PHASE1_DONE:
+ if (lr->lr_status <= 0) {
+ ltd->ltd_namespace_done = 1;
+ list_del_init(<d->ltd_namespace_list);
+ CDEBUG(D_LFSCK, "%s: MDT %x failed/stopped at "
+ "phase1 for namespace LFSCK: rc = %d.\n",
+ lfsck_lfsck2name(lfsck),
+ ltd->ltd_index, lr->lr_status);
+ ns->ln_flags |= LF_INCOMPLETE;
+ fail = true;
+ break;
+ }
+
+ if (lr->lr_flags2 & LF_INCOMPLETE)
+ ns->ln_flags |= LF_INCOMPLETE;
+
+ if (list_empty(<d->ltd_namespace_list))
+ list_add_tail(<d->ltd_namespace_list,
+ &lad->lad_mdt_list);
+ list_add_tail(<d->ltd_namespace_phase_list,
+ &lad->lad_mdt_phase2_list);
+ break;
+ case LE_PHASE2_DONE:
+ ltd->ltd_namespace_done = 1;
+ list_del_init(<d->ltd_namespace_list);
+ break;
+ case LE_PEER_EXIT:
+ fail = true;
+ ltd->ltd_namespace_done = 1;
+ list_del_init(<d->ltd_namespace_list);
+ if (!(lfsck->li_bookmark_ram.lb_param & LPF_FAILOUT)) {
+ CDEBUG(D_LFSCK,
+ "%s: the peer MDT %x exit namespace LFSCK\n",
+ lfsck_lfsck2name(lfsck), ltd->ltd_index);
+ ns->ln_flags |= LF_INCOMPLETE;
+ }
+ break;
+ default:
+ break;
+ }
+ spin_unlock(<ds->ltd_lock);
+
+ if (fail && lfsck->li_bookmark_ram.lb_param & LPF_FAILOUT) {
+ struct lfsck_stop *stop = &lfsck_env_info(env)->lti_stop;
+
+ memset(stop, 0, sizeof(*stop));
+ stop->ls_status = lr->lr_status;
+ stop->ls_flags = lr->lr_param & ~LPF_BROADCAST;
+ lfsck_stop(env, lfsck->li_bottom, stop);
+ } else if (lfsck_phase2_next_ready(lad)) {
+ wake_up_all(&lad->lad_thread.t_ctl_waitq);
+ }
+
+ RETURN(0);
+}
+
+static void lfsck_namespace_repaired(struct lfsck_namespace *ns, __u64 *count)
+{
+ *count += ns->ln_objs_nlink_repaired;
+ *count += ns->ln_dirent_repaired;
+ *count += ns->ln_linkea_repaired;
+ *count += ns->ln_mul_linked_repaired;
+ *count += ns->ln_unmatched_pairs_repaired;
+ *count += ns->ln_dangling_repaired;
+ *count += ns->ln_mul_ref_repaired;
+ *count += ns->ln_bad_type_repaired;
+ *count += ns->ln_lost_dirent_repaired;
+ *count += ns->ln_striped_dirs_disabled;
+ *count += ns->ln_striped_dirs_repaired;
+ *count += ns->ln_striped_shards_repaired;
+ *count += ns->ln_name_hash_repaired;
+ *count += ns->ln_local_lpf_moved;
+}
+
+static int lfsck_namespace_query_all(const struct lu_env *env,
+ struct lfsck_component *com,
+ __u32 *mdts_count, __u64 *repaired)
+{
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ struct lfsck_tgt_descs *ltds = &com->lc_lfsck->li_mdt_descs;
+ struct lfsck_tgt_desc *ltd;
+ int idx;
+ int rc;
+ ENTRY;
+
+ rc = lfsck_query_all(env, com);
+ if (rc != 0)
+ RETURN(rc);
+
+ down_read(<ds->ltd_rw_sem);
+ cfs_foreach_bit(ltds->ltd_tgts_bitmap, idx) {
+ ltd = lfsck_ltd2tgt(ltds, idx);
+ LASSERT(ltd != NULL);
+
+ mdts_count[ltd->ltd_namespace_status]++;
+ *repaired += ltd->ltd_namespace_repaired;
+ }
+ up_read(<ds->ltd_rw_sem);
+
+ down_read(&com->lc_sem);
+ mdts_count[ns->ln_status]++;
+ lfsck_namespace_repaired(ns, repaired);
+ up_read(&com->lc_sem);
+
+ RETURN(0);
+}
+
+static int lfsck_namespace_query(const struct lu_env *env,
+ struct lfsck_component *com,
+ struct lfsck_request *req,
+ struct lfsck_reply *rep,
+ struct lfsck_query *que, int idx)
+{
+ struct lfsck_namespace *ns = com->lc_file_ram;
+ int rc = 0;
+
+ if (que != NULL) {
+ LASSERT(com->lc_lfsck->li_master);
+
+ rc = lfsck_namespace_query_all(env, com,
+ que->lu_mdts_count[idx],
+ &que->lu_repaired[idx]);
+ } else {
+ down_read(&com->lc_sem);
+ rep->lr_status = ns->ln_status;
+ if (req->lr_flags & LEF_QUERY_ALL)
+ lfsck_namespace_repaired(ns, &rep->lr_repaired);
+ up_read(&com->lc_sem);
+ }
+
+ return rc;
+}
+
+static struct lfsck_operations lfsck_namespace_ops = {
+ .lfsck_reset = lfsck_namespace_reset,
+ .lfsck_fail = lfsck_namespace_fail,
+ .lfsck_close_dir = lfsck_namespace_close_dir,
+ .lfsck_open_dir = lfsck_namespace_open_dir,
+ .lfsck_checkpoint = lfsck_namespace_checkpoint,
+ .lfsck_prep = lfsck_namespace_prep,
+ .lfsck_exec_oit = lfsck_namespace_exec_oit,
+ .lfsck_exec_dir = lfsck_namespace_exec_dir,
+ .lfsck_post = lfsck_namespace_post,
+ .lfsck_dump = lfsck_namespace_dump,
+ .lfsck_double_scan = lfsck_namespace_double_scan,
+ .lfsck_data_release = lfsck_namespace_data_release,
+ .lfsck_quit = lfsck_namespace_quit,
+ .lfsck_in_notify = lfsck_namespace_in_notify,
+ .lfsck_query = lfsck_namespace_query,
+};
+
+/**
+ * Repair dangling name entry.
+ *
+ * For the name entry with dangling reference, we need to repare the
+ * inconsistency according to the LFSCK sponsor's requirement: