+static int mdt_hsm_release(struct mdt_thread_info *info, struct mdt_object *o,
+ struct md_attr *ma)
+{
+ struct mdt_lock_handle *lh = &info->mti_lh[MDT_LH_LAYOUT];
+ struct close_data *data;
+ struct ldlm_lock *lease;
+ struct mdt_object *orphan;
+ struct md_attr *orp_ma;
+ struct lu_buf *buf;
+ bool lease_broken;
+ int rc;
+ int rc2;
+ ENTRY;
+
+ if (exp_connect_flags(info->mti_exp) & OBD_CONNECT_RDONLY)
+ RETURN(-EROFS);
+
+ data = req_capsule_client_get(info->mti_pill, &RMF_CLOSE_DATA);
+ if (data == NULL)
+ RETURN(-EPROTO);
+
+ lease = ldlm_handle2lock(&data->cd_handle);
+ if (lease == NULL)
+ RETURN(-ESTALE);
+
+ /* try to hold open_sem so that nobody else can open the file */
+ if (!down_write_trylock(&o->mot_open_sem)) {
+ ldlm_lock_cancel(lease);
+ GOTO(out_reprocess, rc = -EBUSY);
+ }
+
+ /* Check if the lease open lease has already canceled */
+ lock_res_and_lock(lease);
+ lease_broken = ldlm_is_cancel(lease);
+ unlock_res_and_lock(lease);
+
+ LDLM_DEBUG(lease, DFID " lease broken? %d\n",
+ PFID(mdt_object_fid(o)), lease_broken);
+
+ /* Cancel server side lease. Client side counterpart should
+ * have been cancelled. It's okay to cancel it now as we've
+ * held mot_open_sem. */
+ ldlm_lock_cancel(lease);
+
+ if (lease_broken) /* don't perform release task */
+ GOTO(out_unlock, rc = -ESTALE);
+
+ if (fid_is_zero(&data->cd_fid) || !fid_is_sane(&data->cd_fid))
+ GOTO(out_unlock, rc = -EINVAL);
+
+ /* ma_need was set before but it seems fine to change it in order to
+ * avoid modifying the one from RPC */
+ ma->ma_need = MA_HSM;
+ rc = mdt_attr_get_complex(info, o, ma);
+ if (rc != 0)
+ GOTO(out_unlock, rc);
+
+ if (!mdt_hsm_release_allow(ma))
+ GOTO(out_unlock, rc = -EPERM);
+
+ /* already released? */
+ if (ma->ma_hsm.mh_flags & HS_RELEASED)
+ GOTO(out_unlock, rc = 0);
+
+ /* Compare on-disk and packed data_version */
+ if (data->cd_data_version != ma->ma_hsm.mh_arch_ver) {
+ CDEBUG(D_HSM, DFID" data_version mismatches: packed="LPU64
+ " and on-disk="LPU64"\n", PFID(mdt_object_fid(o)),
+ data->cd_data_version, ma->ma_hsm.mh_arch_ver);
+ GOTO(out_unlock, rc = -EPERM);
+ }
+
+ ma->ma_valid = MA_INODE;
+ ma->ma_attr.la_valid &= LA_ATIME | LA_MTIME | LA_CTIME | LA_SIZE;
+ rc = mo_attr_set(info->mti_env, mdt_object_child(o), ma);
+ if (rc < 0)
+ GOTO(out_unlock, rc);
+
+ ma->ma_need = MA_INODE | MA_LOV;
+ rc = mdt_attr_get_complex(info, o, ma);
+ if (rc < 0)
+ GOTO(out_unlock, rc);
+
+ if (!(ma->ma_valid & MA_LOV)) {
+ /* Even empty file are released */
+ memset(ma->ma_lmm, 0, sizeof(*ma->ma_lmm));
+ ma->ma_lmm->lmm_magic = cpu_to_le32(LOV_MAGIC_V1_DEF);
+ ma->ma_lmm->lmm_pattern = cpu_to_le32(LOV_PATTERN_RAID0);
+ ma->ma_lmm->lmm_stripe_size = cpu_to_le32(LOV_MIN_STRIPE_SIZE);
+ ma->ma_lmm_size = sizeof(*ma->ma_lmm);
+ } else {
+ /* Magic must be LOV_MAGIC_Vx_DEF otherwise LOD will interpret
+ * ma_lmm as lov_user_md, then it will be confused by union of
+ * layout_gen and stripe_offset. */
+ if (le32_to_cpu(ma->ma_lmm->lmm_magic) == LOV_MAGIC_V1)
+ ma->ma_lmm->lmm_magic = cpu_to_le32(LOV_MAGIC_V1_DEF);
+ else if (le32_to_cpu(ma->ma_lmm->lmm_magic) == LOV_MAGIC_V3)
+ ma->ma_lmm->lmm_magic = cpu_to_le32(LOV_MAGIC_V3_DEF);
+ else
+ GOTO(out_unlock, rc = -EINVAL);
+ }
+
+ /* Set file as released */
+ ma->ma_lmm->lmm_pattern |= cpu_to_le32(LOV_PATTERN_F_RELEASED);
+
+ orp_ma = &info->mti_u.hsm.attr;
+ orp_ma->ma_attr.la_mode = S_IFREG | S_IWUSR;
+ orp_ma->ma_attr.la_uid = ma->ma_attr.la_uid;
+ orp_ma->ma_attr.la_gid = ma->ma_attr.la_gid;
+ orp_ma->ma_attr.la_valid = LA_MODE | LA_UID | LA_GID;
+ orp_ma->ma_lmm = ma->ma_lmm;
+ orp_ma->ma_lmm_size = ma->ma_lmm_size;
+ orp_ma->ma_valid = MA_INODE | MA_LOV;
+ orphan = mdt_orphan_open(info, info->mti_mdt, &data->cd_fid, orp_ma,
+ FMODE_WRITE);
+ if (IS_ERR(orphan)) {
+ CERROR("%s: cannot open orphan file "DFID": rc = %ld\n",
+ mdt_obd_name(info->mti_mdt), PFID(&data->cd_fid),
+ PTR_ERR(orphan));
+ GOTO(out_unlock, rc = PTR_ERR(orphan));
+ }
+
+ /* Set up HSM attribute for orphan object */
+ CLASSERT(sizeof(struct hsm_attrs) <= sizeof(info->mti_xattr_buf));
+ buf = &info->mti_buf;
+ buf->lb_buf = info->mti_xattr_buf;
+ buf->lb_len = sizeof(struct hsm_attrs);
+ ma->ma_hsm.mh_flags |= HS_RELEASED;
+ lustre_hsm2buf(buf->lb_buf, &ma->ma_hsm);
+ ma->ma_hsm.mh_flags &= ~HS_RELEASED;
+
+ mdt_lock_reg_init(lh, LCK_EX);
+ rc = mdt_object_lock(info, o, lh, MDS_INODELOCK_LAYOUT |
+ MDS_INODELOCK_XATTR, MDT_LOCAL_LOCK);
+ if (rc != 0)
+ GOTO(out_close, rc);
+
+ rc = mo_xattr_set(info->mti_env, mdt_object_child(orphan), buf,
+ XATTR_NAME_HSM, 0);
+
+ if (rc == 0)
+ /* Swap layout with orphan object */
+ rc = mo_swap_layouts(info->mti_env, mdt_object_child(o),
+ mdt_object_child(orphan),
+ SWAP_LAYOUTS_MDS_HSM);
+
+ /* Release exclusive LL */
+ mdt_object_unlock(info, o, lh, 1);
+
+ EXIT;
+
+out_close:
+ /* Close orphan object anyway */
+ rc2 = mo_close(info->mti_env, mdt_object_child(orphan), orp_ma,
+ FMODE_WRITE);
+ if (rc2 < 0)
+ CERROR("%s: error closing volatile file "DFID": rc = %d\n",
+ mdt_obd_name(info->mti_mdt), PFID(&data->cd_fid), rc2);
+ LU_OBJECT_DEBUG(D_HSM, info->mti_env, &orphan->mot_obj,
+ "object closed\n");
+ mdt_object_put(info->mti_env, orphan);
+
+out_unlock:
+ up_write(&o->mot_open_sem);
+
+ /* already released */
+ if (rc == 0) {
+ struct mdt_body *repbody;
+
+ repbody = req_capsule_server_get(info->mti_pill, &RMF_MDT_BODY);
+ LASSERT(repbody != NULL);
+ repbody->mbo_valid |= OBD_MD_CLOSE_INTENT_EXECED;
+ }
+
+out_reprocess:
+ ldlm_reprocess_all(lease->l_resource);
+ LDLM_LOCK_PUT(lease);
+
+ ma->ma_valid = 0;
+ ma->ma_need = 0;
+
+ return rc;
+}
+
+static int mdt_close_swap_layouts(struct mdt_thread_info *info,
+ struct mdt_object *o, struct md_attr *ma)
+{
+ struct mdt_lock_handle *lh1 = &info->mti_lh[MDT_LH_NEW];
+ struct mdt_lock_handle *lh2 = &info->mti_lh[MDT_LH_OLD];
+ struct close_data *data;
+ struct ldlm_lock *lease;
+ struct mdt_object *o1 = o, *o2;
+ bool lease_broken;
+ bool swap_objects;
+ int rc;
+ ENTRY;
+
+ if (exp_connect_flags(info->mti_exp) & OBD_CONNECT_RDONLY)
+ RETURN(-EROFS);
+
+ if (!S_ISREG(lu_object_attr(&o1->mot_obj)))
+ RETURN(-EINVAL);
+
+ data = req_capsule_client_get(info->mti_pill, &RMF_CLOSE_DATA);
+ if (data == NULL)
+ RETURN(-EPROTO);
+
+ if (fid_is_zero(&data->cd_fid) || !fid_is_sane(&data->cd_fid))
+ RETURN(-EINVAL);
+
+ rc = lu_fid_cmp(&data->cd_fid, mdt_object_fid(o));
+ if (unlikely(rc == 0))
+ RETURN(-EINVAL);
+
+ /* Exchange o1 and o2, to enforce locking order */
+ swap_objects = (rc < 0);
+
+ lease = ldlm_handle2lock(&data->cd_handle);
+ if (lease == NULL)
+ RETURN(-ESTALE);
+
+ o2 = mdt_object_find(info->mti_env, info->mti_mdt, &data->cd_fid);
+ if (IS_ERR(o2))
+ GOTO(out_lease, rc = PTR_ERR(o2));
+
+ if (!S_ISREG(lu_object_attr(&o2->mot_obj))) {
+ swap_objects = false; /* not swapped yet */
+ GOTO(out_obj, rc = -EINVAL);
+ }
+
+ if (swap_objects)
+ swap(o1, o2);
+
+ rc = mo_permission(info->mti_env, NULL, mdt_object_child(o1), NULL,
+ MAY_WRITE);
+ if (rc < 0)
+ GOTO(out_obj, rc);
+
+ rc = mo_permission(info->mti_env, NULL, mdt_object_child(o2), NULL,
+ MAY_WRITE);
+ if (rc < 0)
+ GOTO(out_obj, rc);
+
+ /* try to hold open_sem so that nobody else can open the file */
+ if (!down_write_trylock(&o->mot_open_sem)) {
+ ldlm_lock_cancel(lease);
+ GOTO(out_obj, rc = -EBUSY);
+ }
+
+ /* Check if the lease open lease has already canceled */
+ lock_res_and_lock(lease);
+ lease_broken = ldlm_is_cancel(lease);
+ unlock_res_and_lock(lease);
+
+ LDLM_DEBUG(lease, DFID " lease broken? %d\n",
+ PFID(mdt_object_fid(o)), lease_broken);
+
+ /* Cancel server side lease. Client side counterpart should
+ * have been cancelled. It's okay to cancel it now as we've
+ * held mot_open_sem. */
+ ldlm_lock_cancel(lease);
+
+ if (lease_broken)
+ GOTO(out_unlock_sem, rc = -ESTALE);
+
+ mdt_lock_reg_init(lh1, LCK_EX);
+ rc = mdt_object_lock(info, o1, lh1, MDS_INODELOCK_LAYOUT |
+ MDS_INODELOCK_XATTR, MDT_LOCAL_LOCK);
+ if (rc < 0)
+ GOTO(out_unlock_sem, rc);
+
+ mdt_lock_reg_init(lh2, LCK_EX);
+ rc = mdt_object_lock(info, o2, lh2, MDS_INODELOCK_LAYOUT |
+ MDS_INODELOCK_XATTR, MDT_LOCAL_LOCK);
+ if (rc < 0)
+ GOTO(out_unlock1, rc);
+
+ /* Swap layout with orphan object */
+ rc = mo_swap_layouts(info->mti_env, mdt_object_child(o1),
+ mdt_object_child(o2), 0);
+ if (rc < 0)
+ GOTO(out_unlock2, rc);
+
+ EXIT;
+
+out_unlock2:
+ /* Release exclusive LL */
+ mdt_object_unlock(info, o2, lh2, 1);
+
+out_unlock1:
+ mdt_object_unlock(info, o1, lh1, 1);
+
+out_unlock_sem:
+ up_write(&o->mot_open_sem);
+
+ /* already swapped */
+ if (rc == 0) {
+ struct mdt_body *repbody;
+
+ repbody = req_capsule_server_get(info->mti_pill, &RMF_MDT_BODY);
+ LASSERT(repbody != NULL);
+ repbody->mbo_valid |= OBD_MD_CLOSE_INTENT_EXECED;
+ }
+
+out_obj:
+ mdt_object_put(info->mti_env, swap_objects ? o1 : o2);
+
+ ldlm_reprocess_all(lease->l_resource);
+
+out_lease:
+ LDLM_LOCK_PUT(lease);
+
+ ma->ma_valid = 0;
+ ma->ma_need = 0;
+
+ return rc;
+}
+
+#define MFD_CLOSED(mode) ((mode) == MDS_FMODE_CLOSED)