* cache is not cleared yet.
         */
        op_data->op_attr.ia_valid &= ~(TIMES_SET_FLAGS | ATTR_SIZE);
+       if (S_ISREG(inode->i_mode))
+               inode_lock(inode);
        rc = simple_setattr(dentry, &op_data->op_attr);
+       if (S_ISREG(inode->i_mode))
+               inode_unlock(inode);
        op_data->op_attr.ia_valid = ia_valid;
 
        rc = ll_update_inode(inode, &md);
        struct inode *inode = d_inode(dentry);
        struct ll_inode_info *lli = ll_i2info(inode);
        struct md_op_data *op_data = NULL;
-       bool file_is_released = false;
        int rc = 0;
 
        CDEBUG(D_VFSTRACE, "%s: setattr inode "DFID"(%p) from %llu to %llu, valid %x, hsm_import %d\n",
                       LTIME_S(attr->ia_mtime), LTIME_S(attr->ia_ctime),
                       (s64)ktime_get_real_seconds());
 
-       /* We always do an MDS RPC, even if we're only changing the size;
-        * only the MDS knows whether truncate() should fail with -ETXTBUSY
-        */
-
-       op_data = kzalloc(sizeof(*op_data), GFP_NOFS);
-       if (!op_data)
-               return -ENOMEM;
-
-       if (!S_ISDIR(inode->i_mode))
+       if (S_ISREG(inode->i_mode))
                inode_unlock(inode);
 
-       /* truncate on a released file must failed with -ENODATA,
-        * so size must not be set on MDS for released file
-        * but other attributes must be set
+       /*
+        * We always do an MDS RPC, even if we're only changing the size;
+        * only the MDS knows whether truncate() should fail with -ETXTBUSY
         */
-       if (S_ISREG(inode->i_mode)) {
-               struct cl_layout cl = {
-                       .cl_is_released = false,
-               };
-               struct lu_env *env;
-               int refcheck;
-               __u32 gen;
+       op_data = kzalloc(sizeof(*op_data), GFP_NOFS);
+       if (!op_data) {
+               rc = -ENOMEM;
+               goto out;
+       }
 
-               rc = ll_layout_refresh(inode, &gen);
-               if (rc < 0)
-                       goto out;
+       op_data->op_attr = *attr;
 
+       if (!hsm_import && attr->ia_valid & ATTR_SIZE) {
                /*
-                * XXX: the only place we need to know the layout type,
-                * this will be removed by a later patch. -Jinshan
+                * If we are changing file size, file content is
+                * modified, flag it.
                 */
-               env = cl_env_get(&refcheck);
-               if (IS_ERR(env)) {
-                       rc = PTR_ERR(env);
-                       goto out;
-               }
-
-               rc = cl_object_layout_get(env, lli->lli_clob, &cl);
-               cl_env_put(env, &refcheck);
-               if (rc < 0)
-                       goto out;
-
-               file_is_released = cl.cl_is_released;
-
-               if (!hsm_import && attr->ia_valid & ATTR_SIZE) {
-                       if (file_is_released) {
-                               rc = ll_layout_restore(inode, 0, attr->ia_size);
-                               if (rc < 0)
-                                       goto out;
-
-                               file_is_released = false;
-                               ll_layout_refresh(inode, &gen);
-                       }
-
-                       /*
-                        * If we are changing file size, file content is
-                        * modified, flag it.
-                        */
-                       attr->ia_valid |= MDS_OPEN_OWNEROVERRIDE;
-                       op_data->op_bias |= MDS_DATA_MODIFIED;
-               }
+               attr->ia_valid |= MDS_OPEN_OWNEROVERRIDE;
+               op_data->op_bias |= MDS_DATA_MODIFIED;
        }
 
-       memcpy(&op_data->op_attr, attr, sizeof(*attr));
-
        rc = ll_md_setattr(dentry, op_data);
        if (rc)
                goto out;
 
-       if (!S_ISREG(inode->i_mode) || file_is_released) {
+       if (!S_ISREG(inode->i_mode) || hsm_import) {
                rc = 0;
                goto out;
        }
                 */
                rc = cl_setattr_ost(ll_i2info(inode)->lli_clob, attr, 0);
        }
+
+       /*
+        * If the file was restored, it needs to set dirty flag.
+        *
+        * We've already sent MDS_DATA_MODIFIED flag in
+        * ll_md_setattr() for truncate. However, the MDT refuses to
+        * set the HS_DIRTY flag on released files, so we have to set
+        * it again if the file has been restored. Please check how
+        * LLIF_DATA_MODIFIED is set in vvp_io_setattr_fini().
+        *
+        * Please notice that if the file is not released, the previous
+        * MDS_DATA_MODIFIED has taken effect and usually
+        * LLIF_DATA_MODIFIED is not set(see vvp_io_setattr_fini()).
+        * This way we can save an RPC for common open + trunc
+        * operation.
+        */
+       if (test_and_clear_bit(LLIF_DATA_MODIFIED, &lli->lli_flags)) {
+               struct hsm_state_set hss = {
+                       .hss_valid = HSS_SETMASK,
+                       .hss_setmask = HS_DIRTY,
+               };
+               int rc2;
+
+               rc2 = ll_hsm_state_set(inode, &hss);
+               if (rc2 < 0)
+                       CERROR(DFID "HSM set dirty failed: rc2 = %d\n",
+                              PFID(ll_inode2fid(inode)), rc2);
+       }
+
 out:
        if (op_data)
                ll_finish_md_op_data(op_data);
 
-       if (!S_ISDIR(inode->i_mode)) {
+       if (S_ISREG(inode->i_mode)) {
                inode_lock(inode);
                if ((attr->ia_valid & ATTR_SIZE) && !hsm_import)
                        inode_dio_wait(inode);