+ op_data->op_fsuid = from_kuid(&init_user_ns, current_fsuid());
+ op_data->op_fsgid = from_kgid(&init_user_ns, current_fsgid());
+ op_data->op_cap = cfs_curproc_cap_pack();
+
+ tgt = lmv_locate_tgt2(lmv, op_data);
+ if (IS_ERR(tgt))
+ RETURN(PTR_ERR(tgt));
+
+ /*
+ * Cancel UPDATE lock on child (fid1).
+ */
+ op_data->op_flags |= MF_MDC_CANCEL_FID2;
+ rc = lmv_early_cancel(exp, NULL, op_data, tgt->ltd_index, LCK_EX,
+ MDS_INODELOCK_UPDATE, MF_MDC_CANCEL_FID1);
+ if (rc != 0)
+ RETURN(rc);
+
+ rc = md_link(tgt->ltd_exp, op_data, request);
+
+ RETURN(rc);
+}
+
+static int lmv_migrate(struct obd_export *exp, struct md_op_data *op_data,
+ const char *name, size_t namelen,
+ struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_stripe_md *lsm = op_data->op_mea1;
+ struct lmv_tgt_desc *parent_tgt;
+ struct lmv_tgt_desc *sp_tgt;
+ struct lmv_tgt_desc *tp_tgt = NULL;
+ struct lmv_tgt_desc *child_tgt;
+ struct lmv_tgt_desc *tgt;
+ struct lu_fid target_fid;
+ int rc;
+
+ ENTRY;
+
+ LASSERT(op_data->op_cli_flags & CLI_MIGRATE);
+
+ CDEBUG(D_INODE, "MIGRATE "DFID"/%.*s\n",
+ PFID(&op_data->op_fid1), (int)namelen, name);
+
+ op_data->op_fsuid = from_kuid(&init_user_ns, current_fsuid());
+ op_data->op_fsgid = from_kgid(&init_user_ns, current_fsgid());
+ op_data->op_cap = cfs_curproc_cap_pack();
+
+ parent_tgt = lmv_fid2tgt(lmv, &op_data->op_fid1);
+ if (IS_ERR(parent_tgt))
+ RETURN(PTR_ERR(parent_tgt));
+
+ if (lmv_dir_striped(lsm)) {
+ __u32 hash_type = lsm->lsm_md_hash_type;
+ __u32 stripe_count = lsm->lsm_md_stripe_count;
+
+ /*
+ * old stripes are appended after new stripes for migrating
+ * directory.
+ */
+ if (lmv_dir_migrating(lsm)) {
+ hash_type = lsm->lsm_md_migrate_hash;
+ stripe_count -= lsm->lsm_md_migrate_offset;
+ }
+
+ rc = lmv_name_to_stripe_index(hash_type, stripe_count, name,
+ namelen);
+ if (rc < 0)
+ RETURN(rc);
+
+ if (lmv_dir_migrating(lsm))
+ rc += lsm->lsm_md_migrate_offset;
+
+ /* save it in fid4 temporarily for early cancel */
+ op_data->op_fid4 = lsm->lsm_md_oinfo[rc].lmo_fid;
+ sp_tgt = lmv_tgt(lmv, lsm->lsm_md_oinfo[rc].lmo_mds);
+ if (!sp_tgt)
+ RETURN(-ENODEV);
+
+ /*
+ * if parent is being migrated too, fill op_fid2 with target
+ * stripe fid, otherwise the target stripe is not created yet.
+ */
+ if (lmv_dir_migrating(lsm)) {
+ hash_type = lsm->lsm_md_hash_type &
+ ~LMV_HASH_FLAG_MIGRATION;
+ stripe_count = lsm->lsm_md_migrate_offset;
+
+ rc = lmv_name_to_stripe_index(hash_type, stripe_count,
+ name, namelen);
+ if (rc < 0)
+ RETURN(rc);
+
+ op_data->op_fid2 = lsm->lsm_md_oinfo[rc].lmo_fid;
+ tp_tgt = lmv_tgt(lmv, lsm->lsm_md_oinfo[rc].lmo_mds);
+ if (!tp_tgt)
+ RETURN(-ENODEV);
+ }
+ } else {
+ sp_tgt = parent_tgt;
+ }
+
+ child_tgt = lmv_fid2tgt(lmv, &op_data->op_fid3);
+ if (IS_ERR(child_tgt))
+ RETURN(PTR_ERR(child_tgt));
+
+ /* for directory, migrate to MDT specified by lum_stripe_offset;
+ * otherwise migrate to the target stripe of parent, but parent
+ * directory may have finished migration (normally current file too),
+ * allocate FID on MDT lum_stripe_offset, and server will check
+ * whether file was migrated already.
+ */
+ if (S_ISDIR(op_data->op_mode) || !tp_tgt) {
+ struct lmv_user_md *lum = op_data->op_data;
+
+ op_data->op_mds = le32_to_cpu(lum->lum_stripe_offset);
+ } else {
+ op_data->op_mds = tp_tgt->ltd_index;
+ }
+ rc = lmv_fid_alloc(NULL, exp, &target_fid, op_data);
+ if (rc)
+ RETURN(rc);
+
+ /*
+ * for directory, send migrate request to the MDT where the object will
+ * be migrated to, because we can't create a striped directory remotely.
+ *
+ * otherwise, send to the MDT where source is located because regular
+ * file may open lease.
+ *
+ * NB. if MDT doesn't support DIR_MIGRATE, send to source MDT too for
+ * backward compatibility.
+ */
+ if (S_ISDIR(op_data->op_mode) &&
+ (exp_connect_flags2(exp) & OBD_CONNECT2_DIR_MIGRATE)) {
+ tgt = lmv_fid2tgt(lmv, &target_fid);
+ if (IS_ERR(tgt))
+ RETURN(PTR_ERR(tgt));
+ } else {
+ tgt = child_tgt;
+ }
+
+ /* cancel UPDATE lock of parent master object */
+ rc = lmv_early_cancel(exp, parent_tgt, op_data, tgt->ltd_index, LCK_EX,
+ MDS_INODELOCK_UPDATE, MF_MDC_CANCEL_FID1);
+ if (rc)
+ RETURN(rc);