if (d_really_is_positive(child)) {
                status = nfs_ok;
 
+               /* NFSv4 protocol requires change attributes even though
+                * no change happened.
+                */
+               fh_fill_both_attrs(fhp);
+
                switch (open->op_createmode) {
                case NFS4_CREATE_UNCHECKED:
                        if (!d_is_reg(child))
                if (nfsd4_create_is_exclusive(open->op_createmode) && status == 0)
                        open->op_bmval[1] |= (FATTR4_WORD1_TIME_ACCESS |
                                                FATTR4_WORD1_TIME_MODIFY);
-       } else
-               /*
-                * Note this may exit with the parent still locked.
-                * We will hold the lock until nfsd4_open's final
-                * lookup, to prevent renames or unlinks until we've had
-                * a chance to an acquire a delegation if appropriate.
-                */
+       } else {
                status = nfsd_lookup(rqstp, current_fh,
                                     open->op_fname, open->op_fnamelen, *resfh);
+               if (!status)
+                       /* NFSv4 protocol requires change attributes even though
+                        * no change happened.
+                        */
+                       fh_fill_both_attrs(current_fh);
+       }
        if (status)
                goto out;
        status = nfsd_check_obj_isreg(*resfh);
                                    &exp, &dentry);
        if (err)
                return err;
-       fh_unlock(&cstate->current_fh);
        if (d_really_is_negative(dentry)) {
                exp_put(exp);
                err = nfserr_noent;
 
        struct dentry *child;
        __be32 err;
 
-       /* parent may already be locked, and it may get unlocked by
-        * this call, but that is safe.
-        */
        err = nfsd_lookup_dentry(open->op_rqstp, parent,
                                 open->op_fname, open->op_fnamelen,
                                 &exp, &child);
 
                        nfsd4_change_attribute(&fhp->fh_post_attr, inode);
 }
 
+/**
+ * fh_fill_both_attrs - Fill pre-op and post-op attributes
+ * @fhp: file handle to be updated
+ *
+ * This is used when the directory wasn't changed, but wcc attributes
+ * are needed anyway.
+ */
+void fh_fill_both_attrs(struct svc_fh *fhp)
+{
+       fh_fill_post_attrs(fhp);
+       if (!fhp->fh_post_saved)
+               return;
+       fhp->fh_pre_change = fhp->fh_post_change;
+       fhp->fh_pre_mtime = fhp->fh_post_attr.mtime;
+       fhp->fh_pre_ctime = fhp->fh_post_attr.ctime;
+       fhp->fh_pre_size = fhp->fh_post_attr.size;
+       fhp->fh_pre_saved = true;
+}
+
 /*
  * Release a file handle.
  */
 
 
 extern void fh_fill_pre_attrs(struct svc_fh *fhp);
 extern void fh_fill_post_attrs(struct svc_fh *fhp);
-
+extern void fh_fill_both_attrs(struct svc_fh *fhp);
 
 /*
  * Lock a file handle/inode
 
                                goto out_nfserr;
                }
        } else {
-               /*
-                * In the nfsd4_open() case, this may be held across
-                * subsequent open and delegation acquisition which may
-                * need to take the child's i_mutex:
-                */
-               fh_lock_nested(fhp, I_MUTEX_PARENT);
-               dentry = lookup_one_len(name, dparent, len);
+               dentry = lookup_one_len_unlocked(name, dparent, len);
                host_err = PTR_ERR(dentry);
                if (IS_ERR(dentry))
                        goto out_nfserr;
                if (nfsd_mountpoint(dentry, exp)) {
-                       /*
-                        * We don't need the i_mutex after all.  It's
-                        * still possible we could open this (regular
-                        * files can be mountpoints too), but the
-                        * i_mutex is just there to prevent renames of
-                        * something that we might be about to delegate,
-                        * and a mountpoint won't be renamed:
-                        */
-                       fh_unlock(fhp);
-                       if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) {
+                       host_err = nfsd_cross_mnt(rqstp, &dentry, &exp);
+                       if (host_err) {
                                dput(dentry);
                                goto out_nfserr;
                        }
        return nfserrno(host_err);
 }
 
-/*
+/**
+ * nfsd_lookup - look up a single path component for nfsd
+ *
+ * @rqstp:   the request context
+ * @fhp:     the file handle of the directory
+ * @name:    the component name, or %NULL to look up parent
+ * @len:     length of name to examine
+ * @resfh:   pointer to pre-initialised filehandle to hold result.
+ *
  * Look up one component of a pathname.
  * N.B. After this call _both_ fhp and resfh need an fh_put
  *
  * returned. Otherwise the covered directory is returned.
  * NOTE: this mountpoint crossing is not supported properly by all
  *   clients and is explicitly disallowed for NFSv3
- *      NeilBrown <neilb@cse.unsw.edu.au>
+ *
  */
 __be32
 nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
-                               unsigned int len, struct svc_fh *resfh)
+           unsigned int len, struct svc_fh *resfh)
 {
        struct svc_export       *exp;
        struct dentry           *dentry;