dst->ln_unmatched_pairs_repaired =
le64_to_cpu(src->ln_unmatched_pairs_repaired);
dst->ln_dangling_repaired = le64_to_cpu(src->ln_dangling_repaired);
+ dst->ln_mul_ref_repaired = le64_to_cpu(src->ln_mul_ref_repaired);
}
static void lfsck_namespace_cpu_to_le(struct lfsck_namespace *dst,
dst->ln_unmatched_pairs_repaired =
cpu_to_le64(src->ln_unmatched_pairs_repaired);
dst->ln_dangling_repaired = cpu_to_le64(src->ln_dangling_repaired);
+ dst->ln_mul_ref_repaired = cpu_to_le64(src->ln_mul_ref_repaired);
}
static void lfsck_namespace_record_failure(const struct lu_env *env,
return repeated;
}
+/**
+ * Insert orphan into .lustre/lost+found/MDTxxxx/ locally.
+ *
+ * Add the specified orphan MDT-object to the .lustre/lost+found/MDTxxxx/
+ * with the given type to generate the name, the detailed rules for name
+ * have been described as following.
+ *
+ * The function also generates the linkEA corresponding to the name entry
+ * under the .lustre/lost+found/MDTxxxx/ for the orphan MDT-object.
+ *
+ * \param[in] env pointer to the thread context
+ * \param[in] com pointer to the lfsck component
+ * \param[in] orphan pointer to the orphan MDT-object
+ * \param[in] infix additional information for the orphan name, such as
+ * the FID for original
+ * \param[in] type the type for describing why the orphan MDT-object is
+ * created. The rules are as following:
+ *
+ * type "D": The MDT-object is a directory, it may knows its parent
+ * but because there is no valid linkEA, the LFSCK cannot
+ * know where to put it back to the namespace.
+ * type "O": The MDT-object has no linkEA, and there is no name
+ * entry that references the MDT-object.
+ *
+ * \see lfsck_layout_recreate_parent() for more types.
+ *
+ * The orphan name will be like:
+ * ${FID}-${infix}-${type}-${conflict_version}
+ *
+ * \param[out] count if some others inserted some linkEA entries by race,
+ * then return the linkEA entries count.
+ *
+ * \retval positive number for repaired cases
+ * \retval 0 if needs to repair nothing
+ * \retval negative error number on failure
+ */
static int lfsck_namespace_insert_orphan(const struct lu_env *env,
struct lfsck_component *com,
struct dt_object *orphan,
const char *infix, const char *type,
int *count)
{
- /* XXX: TBD */
- return 0;
+ struct lfsck_thread_info *info = lfsck_env_info(env);
+ struct lu_name *cname = &info->lti_name;
+ struct dt_insert_rec *rec = &info->lti_dt_rec;
+ struct lu_fid *tfid = &info->lti_fid5;
+ const struct lu_fid *cfid = lfsck_dto2fid(orphan);
+ const struct lu_fid *pfid;
+ struct lfsck_instance *lfsck = com->lc_lfsck;
+ struct dt_device *dev = lfsck->li_bottom;
+ struct dt_object *parent;
+ struct thandle *th = NULL;
+ struct lustre_handle plh = { 0 };
+ struct lustre_handle clh = { 0 };
+ struct linkea_data ldata = { 0 };
+ struct lu_buf linkea_buf;
+ int namelen;
+ int idx = 0;
+ int rc = 0;
+ bool exist = false;
+ ENTRY;
+
+ cname->ln_name = NULL;
+ /* Create .lustre/lost+found/MDTxxxx when needed. */
+ if (unlikely(lfsck->li_lpf_obj == NULL)) {
+ rc = lfsck_create_lpf(env, lfsck);
+ if (rc != 0)
+ GOTO(log, rc);
+ }
+
+ parent = lfsck->li_lpf_obj;
+ pfid = lfsck_dto2fid(parent);
+
+ /* Hold update lock on the parent to prevent others to access. */
+ rc = lfsck_ibits_lock(env, lfsck, parent, &plh,
+ MDS_INODELOCK_UPDATE, LCK_EX);
+ if (rc != 0)
+ GOTO(log, rc);
+
+ do {
+ namelen = snprintf(info->lti_key, NAME_MAX, DFID"%s-%s-%d",
+ PFID(cfid), infix, type, idx++);
+ rc = dt_lookup(env, parent, (struct dt_rec *)tfid,
+ (const struct dt_key *)info->lti_key,
+ BYPASS_CAPA);
+ if (rc != 0 && rc != -ENOENT)
+ GOTO(log, rc);
+
+ if (unlikely(rc == 0 && lu_fid_eq(cfid, tfid)))
+ exist = true;
+ } while (rc == 0 && !exist);
+
+ cname->ln_name = info->lti_key;
+ cname->ln_namelen = namelen;
+ rc = linkea_data_new(&ldata, &info->lti_linkea_buf2);
+ if (rc != 0)
+ GOTO(log, rc);
+
+ rc = linkea_add_buf(&ldata, cname, pfid);
+ if (rc != 0)
+ GOTO(log, rc);
+
+ rc = lfsck_ibits_lock(env, lfsck, orphan, &clh,
+ MDS_INODELOCK_UPDATE | MDS_INODELOCK_LOOKUP,
+ LCK_EX);
+ if (rc != 0)
+ GOTO(log, rc);
+
+ lfsck_buf_init(&linkea_buf, ldata.ld_buf->lb_buf,
+ ldata.ld_leh->leh_len);
+ th = dt_trans_create(env, dev);
+ if (IS_ERR(th))
+ GOTO(log, rc = PTR_ERR(th));
+
+ if (S_ISDIR(lfsck_object_type(orphan))) {
+ rc = dt_declare_delete(env, orphan,
+ (const struct dt_key *)dotdot, th);
+ if (rc != 0)
+ GOTO(stop, rc);
+
+ rec->rec_type = S_IFDIR;
+ rec->rec_fid = pfid;
+ rc = dt_declare_insert(env, orphan, (const struct dt_rec *)rec,
+ (const struct dt_key *)dotdot, th);
+ if (rc != 0)
+ GOTO(stop, rc);
+ }
+
+ rc = dt_declare_xattr_set(env, orphan, &linkea_buf,
+ XATTR_NAME_LINK, 0, th);
+ if (rc != 0)
+ GOTO(stop, rc);
+
+ if (!exist) {
+ rec->rec_type = lfsck_object_type(orphan) & S_IFMT;
+ rec->rec_fid = cfid;
+ rc = dt_declare_insert(env, parent, (const struct dt_rec *)rec,
+ (const struct dt_key *)cname->ln_name,
+ th);
+ if (rc != 0)
+ GOTO(stop, rc);
+
+ if (S_ISDIR(rec->rec_type)) {
+ rc = dt_declare_ref_add(env, parent, th);
+ if (rc != 0)
+ GOTO(stop, rc);
+ }
+ }
+
+ rc = dt_trans_start_local(env, dev, th);
+ if (rc != 0)
+ GOTO(stop, rc);
+
+ dt_write_lock(env, orphan, 0);
+ rc = lfsck_links_read(env, orphan, &ldata);
+ if (likely((rc == -ENODATA) || (rc == -EINVAL) ||
+ (rc == 0 && ldata.ld_leh->leh_reccount == 0))) {
+ if (lfsck->li_bookmark_ram.lb_param & LPF_DRYRUN)
+ GOTO(unlock, rc = 1);
+
+ if (S_ISDIR(lfsck_object_type(orphan))) {
+ rc = dt_delete(env, orphan,
+ (const struct dt_key *)dotdot, th,
+ BYPASS_CAPA);
+ if (rc != 0)
+ GOTO(unlock, rc);
+
+ rec->rec_type = S_IFDIR;
+ rec->rec_fid = pfid;
+ rc = dt_insert(env, orphan, (const struct dt_rec *)rec,
+ (const struct dt_key *)dotdot, th,
+ BYPASS_CAPA, 1);
+ if (rc != 0)
+ GOTO(unlock, rc);
+ }
+
+ rc = dt_xattr_set(env, orphan, &linkea_buf, XATTR_NAME_LINK, 0,
+ th, BYPASS_CAPA);
+ } else {
+ if (rc == 0 && count != NULL)
+ *count = ldata.ld_leh->leh_reccount;
+
+ GOTO(unlock, rc);
+ }
+ dt_write_unlock(env, orphan);
+
+ if (rc == 0 && !exist) {
+ rec->rec_type = lfsck_object_type(orphan) & S_IFMT;
+ rec->rec_fid = cfid;
+ rc = dt_insert(env, parent, (const struct dt_rec *)rec,
+ (const struct dt_key *)cname->ln_name,
+ th, BYPASS_CAPA, 1);
+ if (rc == 0 && S_ISDIR(rec->rec_type)) {
+ dt_write_lock(env, parent, 0);
+ rc = dt_ref_add(env, parent, th);
+ dt_write_unlock(env, parent);
+ }
+ }
+
+ GOTO(stop, rc = (rc == 0 ? 1 : rc));
+
+unlock:
+ dt_write_unlock(env, orphan);
+
+stop:
+ dt_trans_stop(env, dev, th);
+
+log:
+ lfsck_ibits_unlock(&clh, LCK_EX);
+ lfsck_ibits_unlock(&plh, LCK_EX);
+ CDEBUG(D_LFSCK, "%s: namespace LFSCK insert orphan for the "
+ "object "DFID", name = %s: rc = %d\n",
+ lfsck_lfsck2name(lfsck), PFID(cfid),
+ cname->ln_name != NULL ? cname->ln_name : "<NULL>", rc);
+
+ if (rc != 0) {
+ struct lfsck_namespace *ns = com->lc_file_ram;
+
+ ns->ln_flags |= LF_INCONSISTENT;
+ }
+
+ return rc;
}
static int lfsck_namespace_insert_normal(const struct lu_env *env,
const struct lu_name *cname)
{
struct lfsck_thread_info *info = lfsck_env_info(env);
- struct lu_fid *tfid = &info->lti_fid4;
+ struct lu_fid *tfid = &info->lti_fid5;
struct lu_attr *la = &info->lti_la;
struct dt_insert_rec *rec = &info->lti_dt_rec;
struct lfsck_instance *lfsck = com->lc_lfsck;
* \param[in] obj pointer to the orphan object to be handled
* \param[in] pfid the new fid for the object's ".." name entry
* \param[in,out] lh ldlm lock handler for the given @obj
+ * \param[out] type to tell the caller what the inconsistency is
*
* \retval positive number for repaired cases
* \retval 0 if nothing to be repaired
* \retval negative error number on failure
*/
-static int lfsck_namespace_dsd_orphan(const struct lu_env *env,
- struct lfsck_component *com,
- struct dt_object *obj,
- const struct lu_fid *pfid,
- struct lustre_handle *lh)
+static int
+lfsck_namespace_dsd_orphan(const struct lu_env *env,
+ struct lfsck_component *com,
+ struct dt_object *obj,
+ const struct lu_fid *pfid,
+ struct lustre_handle *lh,
+ enum lfsck_namespace_inconsistency_type *type)
{
struct lfsck_thread_info *info = lfsck_env_info(env);
int rc;
if (rc < 0 && rc != -ENODATA)
RETURN(rc);
+ *type = LNIT_MUL_REF;
/* The unique linkEA is invalid, even if the ".." name entry may be
* valid, we still cannot know via which name entry this directory
* will be referenced. Then handle it as pure orphan. */
*retry = true;
else
rc = lfsck_namespace_dsd_orphan(env, com, child,
- pfid, lh);
+ pfid, lh, type);
GOTO(out, rc);
}
*retry = true;
else
rc = lfsck_namespace_dsd_orphan(env, com, child,
- pfid, lh);
+ pfid, lh, type);
GOTO(out, rc);
}
tfid, cname);
if (rc == 0)
rc = lfsck_namespace_dsd_orphan(env, com, child,
- pfid, lh);
+ pfid, lh, type);
GOTO(out, rc);
}
/* All linkEA entries are invalid and removed, then handle the @child
* as an orphan.*/
if (ldata->ld_leh->leh_reccount == 0) {
- rc = lfsck_namespace_dsd_orphan(env, com, child, pfid, lh);
+ rc = lfsck_namespace_dsd_orphan(env, com, child, pfid, lh,
+ type);
RETURN(rc);
}
* but no parent references this child
* directory, then handle it as orphan. */
lfsck_ibits_unlock(&lh, LCK_EX);
+ type = LNIT_MUL_REF;
snprintf(info->lti_tmpbuf, sizeof(info->lti_tmpbuf),
"-"DFID, PFID(pfid));
rc = lfsck_namespace_insert_orphan(env, com, child,
goto lock;
if (unlikely(ldata.ld_leh->leh_reccount == 0)) {
- rc = lfsck_namespace_dsd_orphan(env, com, child, pfid, &lh);
+ rc = lfsck_namespace_dsd_orphan(env, com, child, pfid, &lh,
+ &type);
GOTO(out, rc);
}
case LNIT_UNMATCHED_PAIRS:
ns->ln_unmatched_pairs_repaired++;
break;
+ case LNIT_MUL_REF:
+ ns->ln_mul_ref_repaired++;
+ break;
default:
break;
}
if (rc < 0)
return rc;
- if (rc > 0)
+ if (rc > 0) {
+ ns->ln_mul_ref_repaired++;
repaired = true;
+ }
}
rc = dt_attr_get(env, child, la, BYPASS_CAPA);
"unknown_inconsistency: "LPU64"\n"
"unmatched_pairs_repaired: "LPU64"\n"
"dangling_repaired: "LPU64"\n"
+ "multiple_referenced_repaired: "LPU64"\n"
"success_count: %u\n"
"run_time_phase1: %u seconds\n"
"run_time_phase2: %u seconds\n",
ns->ln_unknown_inconsistency,
ns->ln_unmatched_pairs_repaired,
ns->ln_dangling_repaired,
+ ns->ln_mul_ref_repaired,
ns->ln_success_count,
time_phase1,
time_phase2);
ns->ln_unknown_inconsistency = 0;
ns->ln_unmatched_pairs_repaired = 0;
ns->ln_dangling_repaired = 0;
+ ns->ln_mul_ref_repaired = 0;
fid_zero(&ns->ln_fid_latest_scanned_phase2);
if (list_empty(&com->lc_link_dir))
list_add_tail(&com->lc_link_dir,
require_dsh_mds || exit 0
+LTIME=${LTIME:-120}
+
SAVED_MDSSIZE=${MDSSIZE}
SAVED_OSTSIZE=${OSTSIZE}
SAVED_OSTCOUNT=${OSTCOUNT}
ALWAYS_EXCEPT="$ALWAYS_EXCEPT 11 12 13 14 15 16 17 18 19 20 21"
[[ $(lustre_version_code $SINGLEMDS) -lt $(version_code 2.6.50) ]] &&
- ALWAYS_EXCEPT="$ALWAYS_EXCEPT 2d 2e 3 22 23"
+ ALWAYS_EXCEPT="$ALWAYS_EXCEPT 2d 2e 3 22 23 24"
build_test_filter
# time to guarantee the status sync up.
wait_update_facet mds${k} "$LCTL get_param -n \
mdd.$(facet_svc mds${k}).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "completed" 32 ||
+ awk '/^status/ { print \\\$2 }'" "completed" $LTIME ||
error "(4) MDS${k} is not the expected 'completed'"
done
# time to guarantee the status sync up.
wait_update_facet mds${k} "$LCTL get_param -n \
mdd.$(facet_svc mds${k}).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "completed" 32 ||
+ awk '/^status/ { print \\\$2 }'" "completed" $LTIME ||
error "(2) MDS${k} is not the expected 'completed'"
done
# time to guarantee the status sync up.
wait_update_facet mds${k} "$LCTL get_param -n \
mdd.$(facet_svc mds${k}).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "completed" 32 ||
+ awk '/^status/ { print \\\$2 }'" "completed" $LTIME ||
error "(2) MDS${k} is not the expected 'completed'"
done
wait_update_facet mds1 "$LCTL get_param -n \
mdd.$(facet_svc mds1).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "scanning-phase2" 32 ||
+ awk '/^status/ { print \\\$2 }'" "scanning-phase2" $LTIME ||
error "(3.0) MDS1 is not the expected 'scanning-phase2'"
do_facet $SINGLEMDS $LCTL set_param fail_val=0 fail_loc=0
# time to guarantee the status sync up.
wait_update_facet mds${k} "$LCTL get_param -n \
mdd.$(facet_svc mds${k}).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "completed" 32 ||
+ awk '/^status/ { print \\\$2 }'" "completed" $LTIME ||
error "(3) MDS${k} is not the expected 'completed'"
done
wait_update_facet mds1 "$LCTL get_param -n \
mdd.$(facet_svc mds1).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "scanning-phase2" 32 ||
+ awk '/^status/ { print \\\$2 }'" "scanning-phase2" $LTIME ||
error "(3) MDS1 is not the expected 'scanning-phase2'"
# to guarantee all updates are synced.
# time to guarantee the status sync up.
wait_update_facet mds${k} "$LCTL get_param -n \
mdd.$(facet_svc mds${k}).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "completed" 32 ||
+ awk '/^status/ { print \\\$2 }'" "completed" $LTIME ||
error "(4) MDS${k} is not the expected 'completed'"
done
# time to guarantee the status sync up.
wait_update_facet mds${k} "$LCTL get_param -n \
mdd.$(facet_svc mds${k}).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "partial" 32 ||
+ awk '/^status/ { print \\\$2 }'" "partial" $LTIME ||
error "(2) MDS${k} is not the expected 'partial'"
done
wait_update_facet ost1 "$LCTL get_param -n \
obdfilter.$(facet_svc ost1).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "partial" 32 || {
+ awk '/^status/ { print \\\$2 }'" "partial" $LTIME || {
error "(3) OST1 is not the expected 'partial'"
}
wait_update_facet ost2 "$LCTL get_param -n \
obdfilter.$(facet_svc ost2).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "completed" 32 || {
+ awk '/^status/ { print \\\$2 }'" "completed" $LTIME || {
error "(4) OST2 is not the expected 'completed'"
}
# time to guarantee the status sync up.
wait_update_facet mds${k} "$LCTL get_param -n \
mdd.$(facet_svc mds${k}).lfsck_layout |
- awk '/^status/ { print \\\$2 }'" "completed" 32 ||
+ awk '/^status/ { print \\\$2 }'" "completed" $LTIME ||
error "(8) MDS${k} is not the expected 'completed'"
done
}
run_test 23c "LFSCK can repair dangling name entry (3)"
+test_24() {
+ [ $MDSCOUNT -lt 2 ] &&
+ skip "We need at least 2 MDSes for this test" && return
+
+ echo "#####"
+ echo "Two MDT-objects back reference the same name entry via their"
+ echo "each own linkEA entry, but the name entry only references one"
+ echo "MDT-object. The namespace LFSCK will remove the linkEA entry"
+ echo "for the MDT-object that is not recognized. If such MDT-object"
+ echo "has no other linkEA entry after the removing, then the LFSCK"
+ echo "will add it as orphan under the .lustre/lost+found/MDTxxxx/."
+ echo "#####"
+
+ check_mount_and_prep
+
+ $LFS mkdir -i 1 $DIR/$tdir/d0 || error "(1) Fail to mkdir d0"
+
+ mkdir $DIR/$tdir/d0/guard || error "(1) Fail to mkdir guard"
+ $LFS path2fid $DIR/$tdir/d0/guard
+
+ mkdir $DIR/$tdir/d0/dummy || error "(2) Fail to mkdir dummy"
+ $LFS path2fid $DIR/$tdir/d0/dummy
+ local pfid=$($LFS path2fid $DIR/$tdir/d0/dummy)
+
+ touch $DIR/$tdir/d0/guard/foo ||
+ error "(3) Fail to touch $DIR/$tdir/d0/guard/foo"
+
+ echo "Inject failure stub on MDT0 to simulate the case that"
+ echo "the $DIR/$tdir/d0/dummy/foo has the 'bad' linkEA entry"
+ echo "that references $DIR/$tdir/d0/guard/foo."
+ echo "Then remove the name entry $DIR/$tdir/d0/dummy/foo."
+ echo "So the MDT-object $DIR/$tdir/d0/dummy/foo will be left"
+ echo "there with the same linkEA entry as another MDT-object"
+ echo "$DIR/$tdir/d0/guard/foo has"
+
+ #define OBD_FAIL_LFSCK_MUL_REF 0x1622
+ do_facet $SINGLEMDS $LCTL set_param fail_loc=0x1622
+ $LFS mkdir -i 0 $DIR/$tdir/d0/dummy/foo ||
+ error "(4) Fail to mkdir $DIR/$tdir/d0/dummy/foo"
+ local cfid=$($LFS path2fid $DIR/$tdir/d0/dummy/foo)
+ rmdir $DIR/$tdir/d0/dummy/foo ||
+ error "(5) Fail to remove $DIR/$tdir/d0/dummy/foo name entry"
+ do_facet $SINGLEMDS $LCTL set_param fail_loc=0
+
+ echo "stat $DIR/$tdir/d0/dummy/foo should fail"
+ stat $DIR/$tdir/d0/dummy/foo > /dev/null 2>&1 &&
+ error "(6) stat successfully unexpectedly"
+
+ echo "Trigger namespace LFSCK to repair multiple-referenced name entry"
+ $START_NAMESPACE -A -r ||
+ error "(7) Fail to start LFSCK for namespace"
+
+ wait_update_facet $SINGLEMDS "$LCTL get_param -n \
+ mdd.${MDT_DEV}.lfsck_namespace |
+ awk '/^status/ { print \\\$2 }'" "completed" 32 || {
+ $SHOW_NAMESPACE
+ error "(8) unexpected status"
+ }
+
+ local repaired=$($SHOW_NAMESPACE |
+ awk '/^multiple_referenced_repaired/ { print $2 }')
+ [ $repaired -eq 1 ] ||
+ error "(9) Fail to repair multiple referenced name entry: $repaired"
+
+ echo "There should be an orphan under .lustre/lost+found/MDT0000/"
+ [ -d $MOUNT/.lustre/lost+found/MDT0000 ] ||
+ error "(10) $MOUNT/.lustre/lost+found/MDT0000/ should be there"
+
+ local cname="$cfid-$pfid-D-0"
+ ls -ail $MOUNT/.lustre/lost+found/MDT0000/$cname ||
+ error "(11) .lustre/lost+found/MDT0000/ should not be empty"
+}
+run_test 24 "LFSCK can repair multiple-referenced name entry"
+
$LCTL set_param debug=-lfsck > /dev/null || true
# restore MDS/OST size