+int mdt_close_handle_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 = NULL;
+ bool lease_broken;
+ bool swap_objects = false;
+ 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 (rc == 0) {
+ /**
+ * only MDS_CLOSE_LAYOUT_SPLIT use the same fid to indicate
+ * mirror deletion, so we'd zero cd_fid, and keeps o2 be NULL.
+ */
+ if (!(ma->ma_attr_flags & MDS_CLOSE_LAYOUT_SPLIT))
+ RETURN(-EINVAL);
+
+ /* zero cd_fid to keeps o2 be NULL */
+ fid_zero(&data->cd_fid);
+ } else if (rc < 0) {
+ /* Exchange o1 and o2, to enforce locking order */
+ swap_objects = true;
+ }
+
+ lease = ldlm_handle2lock(&data->cd_handle);
+ if (lease == NULL)
+ RETURN(-ESTALE);
+
+ if (!fid_is_zero(&data->cd_fid)) {
+ 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 (!mdt_object_exists(o2))
+ GOTO(out_obj, rc = -ENOENT);
+
+ if (!S_ISREG(lu_object_attr(&o2->mot_obj)))
+ 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);
+
+ if (o2) {
+ 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",
+ 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);
+ if (rc < 0)
+ GOTO(out_unlock_sem, rc);
+
+ if (o2) {
+ mdt_lock_reg_init(lh2, LCK_EX);
+ rc = mdt_object_lock(info, o2, lh2, MDS_INODELOCK_LAYOUT |
+ MDS_INODELOCK_XATTR);
+ if (rc < 0)
+ GOTO(out_unlock1, rc);
+ }
+
+ /* Swap layout with orphan object */
+ if (ma->ma_attr_flags & MDS_CLOSE_LAYOUT_SWAP) {
+ rc = mo_swap_layouts(info->mti_env, mdt_object_child(o1),
+ mdt_object_child(o2), 0);
+ } else if (ma->ma_attr_flags & MDS_CLOSE_LAYOUT_MERGE ||
+ ma->ma_attr_flags & MDS_CLOSE_LAYOUT_SPLIT) {
+ struct lu_buf *buf = &info->mti_buf;
+ struct md_rejig_data mrd;
+
+ if (o2) {
+ mrd.mrd_obj = mdt_object_child(o == o1 ? o2 : o1);
+ } else {
+ if (!(ma->ma_attr_flags & MDS_CLOSE_LAYOUT_SPLIT)) {
+ /* paranoid check again */
+ CERROR(DFID
+ ":only mirror split support NULL o2 object\n",
+ PFID(mdt_object_fid(o)));
+ GOTO(out_unlock1, rc = -EINVAL);
+ }
+
+ /* set NULL mrd_obj for deleting mirror objects */
+ mrd.mrd_obj = NULL;
+ }
+
+ if (ma->ma_attr_flags & MDS_CLOSE_LAYOUT_SPLIT) {
+ mrd.mrd_mirror_id = data->cd_mirror_id;
+ /* set a small enough blocks in the SoM */
+ ma->ma_attr.la_blocks >>= 1;
+ }
+
+ buf->lb_len = sizeof(mrd);
+ buf->lb_buf = &mrd;
+ rc = mo_xattr_set(info->mti_env, mdt_object_child(o), buf,
+ XATTR_LUSTRE_LOV,
+ ma->ma_attr_flags & MDS_CLOSE_LAYOUT_SPLIT ?
+ LU_XATTR_SPLIT : LU_XATTR_MERGE);
+ if (rc == 0 && ma->ma_attr.la_valid & (LA_SIZE | LA_BLOCKS |
+ LA_LSIZE | LA_LBLOCKS)) {
+ int rc2;
+ enum lustre_som_flags lsf;
+
+ if (ma->ma_attr.la_valid & (LA_SIZE | LA_BLOCKS))
+ lsf = SOM_FL_STRICT;
+ else
+ lsf = SOM_FL_LAZY;
+
+ mutex_lock(&o->mot_som_mutex);
+ rc2 = mdt_set_som(info, o, lsf,
+ ma->ma_attr.la_size,
+ ma->ma_attr.la_blocks);
+ mutex_unlock(&o->mot_som_mutex);
+ if (rc2 < 0)
+ CERROR(DFID": Setting i_blocks error: %d, "
+ "i_blocks will be reported wrongly and "
+ "can only be fixed in next resync\n",
+ PFID(mdt_object_fid(o)), rc2);
+ }
+ }
+ if (rc < 0)
+ GOTO(out_unlock2, rc);
+
+ EXIT;
+
+out_unlock2:
+ /* Release exclusive LL */
+ if (o2)
+ 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:
+ if (o1 != o)
+ /* the 2nd object has been used, and swapped to o1 */
+ mdt_object_put(info->mti_env, o1);
+ else if (o2)
+ /* the 2nd object has been used, and not swapped */
+ mdt_object_put(info->mti_env, o2);
+
+ ldlm_reprocess_all(lease->l_resource,
+ lease->l_policy_data.l_inodebits.bits);
+
+out_lease:
+ LDLM_LOCK_PUT(lease);
+
+ if (ma != NULL) {
+ ma->ma_valid = 0;
+ ma->ma_need = 0;
+ }
+
+ return rc;
+}
+
+static int mdt_close_resync_done(struct mdt_thread_info *info,
+ struct mdt_object *o, struct md_attr *ma)
+{
+ struct mdt_lock_handle *lhc = &info->mti_lh[MDT_LH_LOCAL];
+ struct close_data *data;
+ struct ldlm_lock *lease;
+ struct md_layout_change layout = { 0 };
+ __u32 *resync_ids = NULL;
+ size_t resync_count = 0;
+ bool lease_broken;
+ int rc;
+
+ ENTRY;
+
+ if (exp_connect_flags(info->mti_exp) & OBD_CONNECT_RDONLY)
+ RETURN(-EROFS);
+
+ if (!S_ISREG(lu_object_attr(&o->mot_obj)))
+ RETURN(-EINVAL);
+
+ data = req_capsule_client_get(info->mti_pill, &RMF_CLOSE_DATA);
+ if (data == NULL)
+ RETURN(-EPROTO);
+
+ if (req_capsule_req_need_swab(info->mti_pill))
+ lustre_swab_close_data_resync_done(&data->cd_resync);
+
+ if (!fid_is_zero(&data->cd_fid))
+ 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);
+
+ resync_count = data->cd_resync.resync_count;
+
+ if (resync_count > INLINE_RESYNC_ARRAY_SIZE) {
+ void *data;
+
+ if (!req_capsule_has_field(info->mti_pill, &RMF_U32,
+ RCL_CLIENT))
+ GOTO(out_unlock, rc = -EPROTO);
+
+ OBD_ALLOC_PTR_ARRAY(resync_ids, resync_count);
+ if (!resync_ids)
+ GOTO(out_unlock, rc = -ENOMEM);
+
+ data = req_capsule_client_get(info->mti_pill, &RMF_U32);
+ memcpy(resync_ids, data, resync_count * sizeof(__u32));
+
+ layout.mlc_resync_ids = resync_ids;
+ } else {
+ layout.mlc_resync_ids = data->cd_resync.resync_ids_inline;
+ }
+
+ layout.mlc_opc = MD_LAYOUT_RESYNC_DONE;
+ layout.mlc_resync_count = resync_count;
+ if (ma->ma_attr.la_valid & (LA_SIZE | LA_BLOCKS)) {
+ layout.mlc_som.lsa_valid = SOM_FL_STRICT;
+ layout.mlc_som.lsa_size = ma->ma_attr.la_size;
+ layout.mlc_som.lsa_blocks = ma->ma_attr.la_blocks;
+ }
+ rc = mdt_layout_change(info, o, lhc, &layout);
+ if (rc)
+ GOTO(out_unlock, rc);
+
+ mdt_object_unlock(info, o, lhc, 0);
+
+ EXIT;
+
+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;
+ }
+
+ if (resync_ids)
+ OBD_FREE_PTR_ARRAY(resync_ids, resync_count);
+
+out_reprocess:
+ ldlm_reprocess_all(lease->l_resource,
+ lease->l_policy_data.l_inodebits.bits);
+ LDLM_LOCK_PUT(lease);
+
+ ma->ma_valid = 0;
+ ma->ma_need = 0;
+
+ return rc;
+}
+
+#define MFD_CLOSED(open_flags) ((open_flags) == MDS_FMODE_CLOSED)