+ rc = mdd_declare_migrate_update(env, spobj, tpobj, tobj, sname, tname,
+ attr, spattr, tpattr, ldata, ma,
+ handle);
+ return rc;
+}
+
+/**
+ * migrate dirent from \a spobj to \a tpobj.
+ **/
+static int mdd_migrate_update(const struct lu_env *env,
+ struct mdd_object *spobj,
+ struct mdd_object *tpobj,
+ struct mdd_object *obj,
+ const struct lu_name *sname,
+ const struct lu_name *tname,
+ struct lu_attr *attr,
+ struct lu_attr *spattr,
+ struct lu_attr *tpattr,
+ struct linkea_data *ldata,
+ struct md_attr *ma,
+ struct thandle *handle)
+{
+ struct mdd_thread_info *info = mdd_env_info(env);
+ struct lu_attr *la = &info->mti_la_for_fix;
+ int rc;
+
+ ENTRY;
+
+ CDEBUG(D_INFO, "update "DFID" from "DFID"/%s to "DFID"/%s\n",
+ PFID(mdd_object_fid(obj)), PFID(mdd_object_fid(spobj)),
+ sname->ln_name, PFID(mdd_object_fid(tpobj)), tname->ln_name);
+
+ rc = __mdd_index_delete(env, spobj, sname->ln_name,
+ S_ISDIR(attr->la_mode), handle);
+ if (rc)
+ RETURN(rc);
+
+ rc = __mdd_index_insert(env, tpobj, mdd_object_fid(obj),
+ attr->la_mode & S_IFMT,
+ tname->ln_name, handle);
+ if (rc)
+ RETURN(rc);
+
+ rc = mdd_links_write(env, obj, ldata, handle);
+ if (rc)
+ RETURN(rc);
+
+ la->la_ctime = la->la_mtime = ma->ma_attr.la_ctime;
+ la->la_valid = LA_CTIME | LA_MTIME;
+ mdd_write_lock(env, spobj, DT_SRC_PARENT);
+ rc = mdd_update_time(env, spobj, spattr, la, handle);
+ mdd_write_unlock(env, spobj);
+ if (rc)
+ RETURN(rc);
+
+ if (tpobj != spobj) {
+ la->la_valid = LA_CTIME | LA_MTIME;
+ mdd_write_lock(env, tpobj, DT_TGT_PARENT);
+ rc = mdd_update_time(env, tpobj, tpattr, la, handle);
+ mdd_write_unlock(env, tpobj);
+ if (rc)
+ RETURN(rc);
+ }
+
+ RETURN(rc);
+}
+
+/**
+ * Migrate file/dir to target MDT.
+ *
+ * Create target according to \a spec, and then migrate xattrs, if it's
+ * directory, migrate source stripes to target.
+ *
+ * \param[in] env execution environment
+ * \param[in] spobj source parent object
+ * \param[in] tpobj target parent object
+ * \param[in] sobj source object
+ * \param[in] tobj target object
+ * \param[in] lname file name
+ * \param[in] spattr source parent attributes
+ * \param[in] tpattr target parent attributes
+ * \param[in] attr source attributes
+ * \param[in] sbuf source LMV buf
+ * \param[in] spec migrate create spec
+ * \param[in] hint target creation hint
+ * \param[in] handle tranasction handle
+ *
+ * \retval 0 on success
+ * \retval -errno on failure
+ **/
+static int mdd_migrate_create(const struct lu_env *env,
+ struct mdd_object *spobj,
+ struct mdd_object *tpobj,
+ struct mdd_object *sobj,
+ struct mdd_object *tobj,
+ const struct lu_name *sname,
+ const struct lu_name *tname,
+ struct lu_attr *spattr,
+ struct lu_attr *tpattr,
+ struct lu_attr *attr,
+ const struct lu_buf *sbuf,
+ struct linkea_data *ldata,
+ struct md_attr *ma,
+ struct md_op_spec *spec,
+ struct dt_allocation_hint *hint,
+ struct thandle *handle)
+{
+ int rc;
+
+ ENTRY;
+
+ /*
+ * migrate sobj stripes to tobj if it's directory:
+ * 1. detach stripes from sobj.
+ * 2. attach stripes to tobj, see mdd_declare_migrate_mdt().
+ * 3. create stripes for tobj, see lod_xattr_set_lmv().
+ */
+ if (S_ISDIR(attr->la_mode)) {
+ struct mdd_thread_info *info = mdd_env_info(env);
+ struct md_layout_change *mlc = &info->mti_mlc;
+
+ mlc->mlc_opc = MD_LAYOUT_DETACH;
+
+ mdd_write_lock(env, sobj, DT_SRC_PARENT);
+ rc = mdo_layout_change(env, sobj, mlc, handle);
+ mdd_write_unlock(env, sobj);
+ if (rc)
+ RETURN(rc);
+ }
+
+ /* don't set nlink from sobj */
+ attr->la_valid &= ~LA_NLINK;
+
+ rc = mdd_create_object(env, tpobj, tobj, attr, spec, NULL, NULL, NULL,
+ hint, handle, false);
+ if (rc)
+ RETURN(rc);
+
+ mdd_write_lock(env, tobj, DT_TGT_CHILD);
+ rc = mdd_iterate_xattrs(env, sobj, tobj, true, handle, mdo_xattr_set);
+ mdd_write_unlock(env, tobj);
+ if (rc)
+ RETURN(rc);
+
+ /* for regular file, update OST objects XATTR_NAME_FID */
+ if (S_ISREG(attr->la_mode)) {
+ struct lu_buf fid_buf;
+
+ /* target may be remote, update PFID via sobj. */
+ fid_buf.lb_buf = (void *)mdd_object_fid(tobj);
+ fid_buf.lb_len = sizeof(struct lu_fid);
+ rc = mdo_xattr_set(env, sobj, &fid_buf, XATTR_NAME_FID, 0,
+ handle);
+ if (rc)
+ RETURN(rc);
+
+ /* delete LOV to avoid deleting OST objs when destroying sobj */
+ mdd_write_lock(env, sobj, DT_SRC_CHILD);
+ rc = mdo_xattr_del(env, sobj, XATTR_NAME_LOV, handle);
+ mdd_write_unlock(env, sobj);
+ /* O_DELAY_CREATE file may not have LOV, ignore -ENODATA */
+ if (rc && rc != -ENODATA)
+ RETURN(rc);
+ rc = 0;
+ }
+
+ /* update links FID */
+ if (!S_ISDIR(attr->la_mode)) {
+ rc = mdd_iterate_linkea(env, sobj, tobj, tname,
+ mdd_object_fid(tpobj), ldata,
+ NULL, handle, mdd_update_link);
+ if (rc)
+ RETURN(rc);
+ }
+
+ /* don't destroy sobj if it's plain directory */
+ if (!S_ISDIR(attr->la_mode) || sbuf->lb_buf) {
+ mdd_write_lock(env, sobj, DT_SRC_CHILD);
+ rc = mdo_ref_del(env, sobj, handle);
+ if (!rc) {
+ if (S_ISDIR(attr->la_mode))
+ rc = mdo_ref_del(env, sobj, handle);
+ if (!rc)
+ rc = mdo_destroy(env, sobj, handle);
+ }
+ mdd_write_unlock(env, sobj);
+ if (rc)
+ RETURN(rc);
+ }
+
+ rc = mdd_migrate_update(env, spobj, tpobj, tobj, sname, tname, attr,
+ spattr, tpattr, ldata, ma, handle);
+
+ RETURN(rc);
+}
+
+/* NB: if user issued different migrate command, we can't adjust it silently
+ * here, because this command will decide target MDT in subdir migration in
+ * LMV.
+ */
+static int mdd_migrate_cmd_check(struct mdd_device *mdd,
+ const struct lmv_mds_md_v1 *lmv,
+ const struct lmv_user_md_v1 *lum,
+ const struct lu_name *lname)
+{
+ __u32 lum_stripe_count = lum->lum_stripe_count;
+ __u32 lmv_hash_type = lmv->lmv_hash_type;
+
+ if (!lmv_is_sane(lmv))
+ return -EBADF;
+
+ /* if stripe_count unspecified, set to 1 */
+ if (!lum_stripe_count)
+ lum_stripe_count = cpu_to_le32(1);
+
+ lmv_hash_type &= ~cpu_to_le32(LMV_HASH_FLAG_MIGRATION);
+
+ /* TODO: check specific MDTs */
+ if (lum_stripe_count != lmv->lmv_migrate_offset ||
+ lum->lum_stripe_offset != lmv->lmv_master_mdt_index ||
+ (lum->lum_hash_type && lum->lum_hash_type != lmv_hash_type)) {
+ CERROR("%s: '"DNAME"' migration was interrupted, run 'lfs migrate -m %d -c %d -H %s "DNAME"' to finish migration.\n",
+ mdd2obd_dev(mdd)->obd_name, PNAME(lname),
+ le32_to_cpu(lmv->lmv_master_mdt_index),
+ le32_to_cpu(lmv->lmv_migrate_offset),
+ mdt_hash_name[le32_to_cpu(lmv_hash_type)],
+ PNAME(lname));
+ return -EPERM;
+ }
+
+ return -EALREADY;
+}
+
+/**
+ * Internal function to migrate directory or file between MDTs.
+ *
+ * migrate source to target in following steps:
+ * 1. create target, append source stripes after target's if it's directory,
+ * migrate xattrs and update fid of source links.
+ * 2. update namespace: migrate dirent from source parent to target parent,
+ * update file linkea, and destroy source if it's not needed any more.
+ *
+ * \param[in] env execution environment
+ * \param[in] spobj source parent object
+ * \param[in] tpobj target parent object
+ * \param[in] sobj source object
+ * \param[in] tobj target object
+ * \param[in] sname source file name
+ * \param[in] tname target file name
+ * \param[in] spec target creation spec
+ * \param[in] ma used to update \a pobj mtime and ctime
+ *
+ * \retval 0 on success
+ * \retval -errno on failure
+ */
+static int mdd_migrate_object(const struct lu_env *env,
+ struct mdd_object *spobj,
+ struct mdd_object *tpobj,
+ struct mdd_object *sobj,
+ struct mdd_object *tobj,
+ const struct lu_name *sname,
+ const struct lu_name *tname,
+ struct md_op_spec *spec,
+ struct md_attr *ma)
+{
+ struct mdd_thread_info *info = mdd_env_info(env);
+ struct mdd_device *mdd = mdo2mdd(&spobj->mod_obj);
+ struct lu_attr *spattr = &info->mti_pattr;
+ struct lu_attr *tpattr = &info->mti_tpattr;
+ struct lu_attr *attr = &info->mti_cattr;
+ struct linkea_data *ldata = &info->mti_link_data;
+ struct dt_allocation_hint *hint = &info->mti_hint;
+ struct lu_buf sbuf = { NULL };
+ struct lmv_mds_md_v1 *lmv;
+ struct thandle *handle;
+ int rc;
+
+ ENTRY;
+
+ rc = mdd_la_get(env, sobj, attr);
+ if (rc)
+ RETURN(rc);
+
+ rc = mdd_la_get(env, spobj, spattr);
+ if (rc)
+ RETURN(rc);
+
+ rc = mdd_la_get(env, tpobj, tpattr);
+ if (rc)
+ RETURN(rc);
+
+ if (S_ISDIR(attr->la_mode) && !spec->sp_migrate_nsonly) {
+ struct lmv_user_md_v1 *lum = spec->u.sp_ea.eadata;
+
+ LASSERT(lum);
+
+ /* if user use default value '0' for stripe_count, we need to
+ * adjust it to '1' to create a 1-stripe directory.
+ */
+ if (lum->lum_stripe_count == 0)
+ lum->lum_stripe_count = cpu_to_le32(1);
+
+ rc = mdd_stripe_get(env, sobj, &sbuf, XATTR_NAME_LMV);
+ if (rc && rc != -ENODATA)
+ GOTO(out, rc);
+
+ lmv = sbuf.lb_buf;
+ if (lmv) {
+ if (!lmv_is_sane(lmv))
+ GOTO(out, rc = -EBADF);
+ if (lmv_is_migrating(lmv)) {
+ rc = mdd_migrate_cmd_check(mdd, lmv, lum,
+ sname);
+ GOTO(out, rc);
+ }
+ }
+ } else if (!S_ISDIR(attr->la_mode)) {
+ if (spobj == tpobj)
+ GOTO(out, rc = -EALREADY);
+
+ /* update namespace only if @sobj is on MDT where @tpobj is. */
+ if (!mdd_object_remote(tpobj) && !mdd_object_remote(sobj))
+ spec->sp_migrate_nsonly = true;
+
+ if (S_ISLNK(attr->la_mode)) {
+ lu_buf_check_and_alloc(&sbuf, attr->la_size + 1);
+ if (!sbuf.lb_buf)
+ GOTO(out, rc = -ENOMEM);
+
+ rc = mdd_readlink(env, &sobj->mod_obj, &sbuf);
+ if (rc <= 0) {
+ rc = rc ?: -EFAULT;
+ CERROR("%s: "DFID" readlink failed: rc = %d\n",
+ mdd2obd_dev(mdd)->obd_name,
+ PFID(mdd_object_fid(sobj)), rc);
+ GOTO(out, rc);
+ }
+ }
+ }
+
+ /* linkea needs update upon FID or parent stripe change */
+ rc = mdd_migrate_linkea_prepare(env, mdd, spobj, tpobj, sobj, sname,
+ tname, attr, ldata);
+ if (rc > 0)
+ /* update namespace only if @sobj has link on its MDT. */
+ spec->sp_migrate_nsonly = true;
+ else if (rc < 0)
+ GOTO(out, rc);
+
+ rc = mdd_migrate_sanity_check(env, mdd, spobj, tpobj, sobj, tobj,
+ spattr, tpattr, attr);
+ if (rc)
+ GOTO(out, rc);
+
+ handle = mdd_trans_create(env, mdd);
+ if (IS_ERR(handle))
+ GOTO(out, rc = PTR_ERR(handle));
+
+ if (spec->sp_migrate_nsonly)
+ rc = mdd_declare_migrate_update(env, spobj, tpobj, sobj, sname,
+ tname, attr, spattr, tpattr,
+ ldata, ma, handle);
+ else
+ rc = mdd_declare_migrate_create(env, spobj, tpobj, sobj, tobj,
+ sname, tname, spattr, tpattr,
+ attr, &sbuf, ldata, ma, spec,
+ hint, handle);
+ if (rc)
+ GOTO(stop, rc);
+
+ rc = mdd_declare_changelog_store(env, mdd, CL_MIGRATE, tname, sname,
+ handle);
+ if (rc)
+ GOTO(stop, rc);
+
+ rc = mdd_trans_start(env, mdd, handle);
+ if (rc)
+ GOTO(stop, rc);
+
+ if (spec->sp_migrate_nsonly)
+ rc = mdd_migrate_update(env, spobj, tpobj, sobj, sname, tname,
+ attr, spattr, tpattr, ldata, ma,
+ handle);
+ else
+ rc = mdd_migrate_create(env, spobj, tpobj, sobj, tobj, sname,
+ tname, spattr, tpattr, attr, &sbuf,
+ ldata, ma, spec, hint, handle);
+ if (rc)
+ GOTO(stop, rc);
+
+ rc = mdd_changelog_ns_store(env, mdd, CL_MIGRATE, 0,
+ spec->sp_migrate_nsonly ? sobj : tobj,
+ mdd_object_fid(spobj), mdd_object_fid(sobj),
+ mdd_object_fid(tpobj), tname, sname,
+ handle);
+ if (rc)
+ GOTO(stop, rc);
+ EXIT;
+
+stop:
+ rc = mdd_trans_stop(env, mdd, rc, handle);
+out:
+ lu_buf_free(&sbuf);
+
+ return rc;
+}
+
+/**
+ * Migrate directory or file between MDTs.
+ *
+ * \param[in] env execution environment
+ * \param[in] md_pobj parent master object
+ * \param[in] md_sobj source object
+ * \param[in] lname file name
+ * \param[in] md_tobj target object
+ * \param[in] spec target creation spec
+ * \param[in] ma used to update \a pobj mtime and ctime
+ *
+ * \retval 0 on success
+ * \retval -errno on failure
+ */
+static int mdd_migrate(const struct lu_env *env, struct md_object *md_pobj,
+ struct md_object *md_sobj, const struct lu_name *lname,
+ struct md_object *md_tobj, struct md_op_spec *spec,
+ struct md_attr *ma)
+{
+ struct mdd_thread_info *info = mdd_env_info(env);
+ struct mdd_device *mdd = mdo2mdd(md_pobj);
+ struct mdd_object *pobj = md2mdd_obj(md_pobj);
+ struct mdd_object *sobj = md2mdd_obj(md_sobj);
+ struct mdd_object *tobj = md2mdd_obj(md_tobj);
+ struct mdd_object *spobj = NULL;
+ struct mdd_object *tpobj = NULL;
+ struct lu_buf pbuf = { NULL };
+ struct lu_fid *fid = &info->mti_fid2;
+ struct lmv_mds_md_v1 *lmv;
+ int rc;
+
+ ENTRY;
+
+ /* locate source and target stripe on pobj, which are the real parent */
+ rc = mdd_stripe_get(env, pobj, &pbuf, XATTR_NAME_LMV);
+ if (rc < 0 && rc != -ENODATA)
+ RETURN(rc);
+
+ lmv = pbuf.lb_buf;
+ if (lmv) {
+ int index;
+
+ if (!lmv_is_sane(lmv))
+ GOTO(out, rc = -EBADF);
+
+ /* locate target parent stripe */
+ /* fail check here to make sure top dir migration succeed. */
+ if (lmv_is_migrating(lmv) &&
+ OBD_FAIL_CHECK_RESET(OBD_FAIL_MIGRATE_ENTRIES, 0))
+ GOTO(out, rc = -EIO);
+
+ index = lmv_name_to_stripe_index(lmv, lname->ln_name,
+ lname->ln_namelen);
+ if (index < 0)
+ GOTO(out, rc = index);
+
+ fid_le_to_cpu(fid, &lmv->lmv_stripe_fids[index]);
+ tpobj = mdd_object_find(env, mdd, fid);
+ if (IS_ERR(tpobj))
+ GOTO(out, rc = PTR_ERR(tpobj));
+
+ /* locate source parent stripe */
+ if (lmv_is_layout_changing(lmv)) {
+ index = lmv_name_to_stripe_index_old(lmv,
+ lname->ln_name,
+ lname->ln_namelen);
+ if (index < 0)
+ GOTO(out, rc = index);
+
+ fid_le_to_cpu(fid, &lmv->lmv_stripe_fids[index]);
+ spobj = mdd_object_find(env, mdd, fid);
+ if (IS_ERR(spobj))
+ GOTO(out, rc = PTR_ERR(spobj));
+
+ /* parent stripe unchanged */
+ if (spobj == tpobj) {
+ if (!lmv_is_restriping(lmv))
+ GOTO(out, rc = -EINVAL);
+ GOTO(out, rc = -EALREADY);
+ }
+ } else {
+ spobj = tpobj;
+ mdd_object_get(spobj);
+ }
+ } else {
+ tpobj = pobj;
+ spobj = pobj;
+ mdd_object_get(tpobj);
+ mdd_object_get(spobj);
+ }
+
+ rc = mdd_migrate_object(env, spobj, tpobj, sobj, tobj, lname, lname,
+ spec, ma);
+ GOTO(out, rc);
+
+out:
+ if (!IS_ERR_OR_NULL(spobj))
+ mdd_object_put(env, spobj);
+ if (!IS_ERR_OR_NULL(tpobj))
+ mdd_object_put(env, tpobj);
+ lu_buf_free(&pbuf);
+
+ return rc;
+}
+
+static int mdd_declare_1sd_collapse(const struct lu_env *env,
+ struct mdd_object *pobj,
+ struct mdd_object *obj,
+ struct mdd_object *stripe,
+ struct lu_attr *attr,
+ struct md_layout_change *mlc,
+ struct lu_name *lname,
+ struct thandle *handle)
+{
+ int rc;
+
+ mlc->mlc_opc = MD_LAYOUT_DETACH;
+ rc = mdo_declare_layout_change(env, obj, mlc, handle);
+ if (rc)
+ return rc;
+
+ rc = mdo_declare_index_insert(env, stripe, mdd_object_fid(pobj),
+ S_IFDIR, dotdot, handle);
+ if (rc)
+ return rc;
+
+ rc = mdd_iterate_xattrs(env, obj, stripe, false, handle,
+ mdo_declare_xattr_set);
+ if (rc)
+ return rc;
+
+ rc = mdo_declare_xattr_del(env, stripe, XATTR_NAME_LMV, handle);
+ if (rc)
+ return rc;
+
+ rc = mdo_declare_attr_set(env, stripe, attr, handle);
+ if (rc)
+ return rc;
+
+ rc = mdo_declare_index_delete(env, pobj, lname->ln_name, handle);
+ if (rc)
+ return rc;
+
+ rc = mdo_declare_index_insert(env, pobj, mdd_object_fid(stripe),
+ attr->la_mode, lname->ln_name, handle);
+ if (rc)
+ return rc;
+
+ rc = mdo_declare_ref_del(env, obj, handle);
+ if (rc)
+ return rc;
+
+ rc = mdo_declare_ref_del(env, obj, handle);
+ if (rc)
+ return rc;
+
+ rc = mdo_declare_destroy(env, obj, handle);
+ if (rc)
+ return rc;
+
+ return rc;
+}
+
+/* transform one-stripe directory to a plain directory */
+static int mdd_1sd_collapse(const struct lu_env *env,
+ struct mdd_object *pobj,
+ struct mdd_object *obj,
+ struct mdd_object *stripe,
+ struct lu_attr *attr,
+ struct md_layout_change *mlc,
+ struct lu_name *lname,
+ struct thandle *handle)
+{
+ int rc;
+
+ ENTRY;
+
+ /* replace 1-stripe directory with its stripe */
+ mlc->mlc_opc = MD_LAYOUT_DETACH;
+
+ mdd_write_lock(env, obj, DT_SRC_PARENT);
+ rc = mdo_layout_change(env, obj, mlc, handle);
+ mdd_write_unlock(env, obj);
+ if (rc)
+ RETURN(rc);
+
+ mdd_write_lock(env, pobj, DT_SRC_PARENT);
+ mdd_write_lock(env, obj, DT_SRC_CHILD);
+
+ /* insert dotdot to stripe which points to parent */
+ rc = __mdd_index_insert_only(env, stripe, mdd_object_fid(pobj),
+ S_IFDIR, dotdot, handle);
+ if (rc)
+ GOTO(out, rc);
+
+ /* copy xattrs including linkea */
+ rc = mdd_iterate_xattrs(env, obj, stripe, false, handle, mdo_xattr_set);
+ if (rc)
+ GOTO(out, rc);
+
+ /* delete LMV */
+ rc = mdo_xattr_del(env, stripe, XATTR_NAME_LMV, handle);
+ if (rc)
+ GOTO(out, rc);