ARM: tegra: pm375 : Enable utmi1
[linux-3.10.git] / fs / 9p / vfs_inode.c
index 8f68280..d86edc8 100644 (file)
@@ -23,6 +23,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 
 static const struct inode_operations v9fs_dir_inode_operations;
 static const struct inode_operations v9fs_dir_inode_operations_dotu;
-static const struct inode_operations v9fs_dir_inode_operations_dotl;
 static const struct inode_operations v9fs_file_inode_operations;
-static const struct inode_operations v9fs_file_inode_operations_dotl;
 static const struct inode_operations v9fs_symlink_inode_operations;
-static const struct inode_operations v9fs_symlink_inode_operations_dotl;
-
-static int
-v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
-                   dev_t rdev);
 
 /**
  * unixmode2p9mode - convert unix mode bits to plan 9
@@ -66,15 +61,13 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
  *
  */
 
-static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode)
+static u32 unixmode2p9mode(struct v9fs_session_info *v9ses, umode_t mode)
 {
        int res;
        res = mode & 0777;
        if (S_ISDIR(mode))
                res |= P9_DMDIR;
        if (v9fs_proto_dotu(v9ses)) {
-               if (S_ISLNK(mode))
-                       res |= P9_DMSYMLINK;
                if (v9ses->nodev == 0) {
                        if (S_ISSOCK(mode))
                                res |= P9_DMSOCKET;
@@ -92,25 +85,51 @@ static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode)
                        res |= P9_DMSETGID;
                if ((mode & S_ISVTX) == S_ISVTX)
                        res |= P9_DMSETVTX;
-               if ((mode & P9_DMLINK))
-                       res |= P9_DMLINK;
        }
+       return res;
+}
+
+/**
+ * p9mode2perm- convert plan9 mode bits to unix permission bits
+ * @v9ses: v9fs session information
+ * @stat: p9_wstat from which mode need to be derived
+ *
+ */
+static int p9mode2perm(struct v9fs_session_info *v9ses,
+                      struct p9_wstat *stat)
+{
+       int res;
+       int mode = stat->mode;
 
+       res = mode & S_IALLUGO;
+       if (v9fs_proto_dotu(v9ses)) {
+               if ((mode & P9_DMSETUID) == P9_DMSETUID)
+                       res |= S_ISUID;
+
+               if ((mode & P9_DMSETGID) == P9_DMSETGID)
+                       res |= S_ISGID;
+
+               if ((mode & P9_DMSETVTX) == P9_DMSETVTX)
+                       res |= S_ISVTX;
+       }
        return res;
 }
 
 /**
  * p9mode2unixmode- convert plan9 mode bits to unix mode bits
  * @v9ses: v9fs session information
- * @mode: mode to convert
+ * @stat: p9_wstat from which mode need to be derived
+ * @rdev: major number, minor number in case of device files.
  *
  */
-
-static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)
+static umode_t p9mode2unixmode(struct v9fs_session_info *v9ses,
+                              struct p9_wstat *stat, dev_t *rdev)
 {
        int res;
+       u32 mode = stat->mode;
 
-       res = mode & 0777;
+       *rdev = 0;
+       res = p9mode2perm(v9ses, stat);
 
        if ((mode & P9_DMDIR) == P9_DMDIR)
                res |= S_IFDIR;
@@ -123,21 +142,26 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)
                 && (v9ses->nodev == 0))
                res |= S_IFIFO;
        else if ((mode & P9_DMDEVICE) && (v9fs_proto_dotu(v9ses))
-                && (v9ses->nodev == 0))
-               res |= S_IFBLK;
-       else
-               res |= S_IFREG;
-
-       if (v9fs_proto_dotu(v9ses)) {
-               if ((mode & P9_DMSETUID) == P9_DMSETUID)
-                       res |= S_ISUID;
-
-               if ((mode & P9_DMSETGID) == P9_DMSETGID)
-                       res |= S_ISGID;
+                && (v9ses->nodev == 0)) {
+               char type = 0, ext[32];
+               int major = -1, minor = -1;
 
-               if ((mode & P9_DMSETVTX) == P9_DMSETVTX)
-                       res |= S_ISVTX;
-       }
+               strncpy(ext, stat->extension, sizeof(ext));
+               sscanf(ext, "%c %u %u", &type, &major, &minor);
+               switch (type) {
+               case 'c':
+                       res |= S_IFCHR;
+                       break;
+               case 'b':
+                       res |= S_IFBLK;
+                       break;
+               default:
+                       p9_debug(P9_DEBUG_ERROR, "Unknown special type %c %s\n",
+                                type, stat->extension);
+               };
+               *rdev = MKDEV(major, minor);
+       } else
+               res |= S_IFREG;
 
        return res;
 }
@@ -168,9 +192,6 @@ int v9fs_uflags2omode(int uflags, int extended)
                break;
        }
 
-       if (uflags & O_TRUNC)
-               ret |= P9_OTRUNC;
-
        if (extended) {
                if (uflags & O_EXCL)
                        ret |= P9_OEXCL;
@@ -204,32 +225,31 @@ v9fs_blank_wstat(struct p9_wstat *wstat)
        wstat->uid = NULL;
        wstat->gid = NULL;
        wstat->muid = NULL;
-       wstat->n_uid = ~0;
-       wstat->n_gid = ~0;
-       wstat->n_muid = ~0;
+       wstat->n_uid = INVALID_UID;
+       wstat->n_gid = INVALID_GID;
+       wstat->n_muid = INVALID_UID;
        wstat->extension = NULL;
 }
 
-#ifdef CONFIG_9P_FSCACHE
 /**
  * v9fs_alloc_inode - helper function to allocate an inode
- * This callback is executed before setting up the inode so that we
- * can associate a vcookie with each inode.
  *
  */
-
 struct inode *v9fs_alloc_inode(struct super_block *sb)
 {
-       struct v9fs_cookie *vcookie;
-       vcookie = (struct v9fs_cookie *)kmem_cache_alloc(vcookie_cache,
-                                                        GFP_KERNEL);
-       if (!vcookie)
+       struct v9fs_inode *v9inode;
+       v9inode = (struct v9fs_inode *)kmem_cache_alloc(v9fs_inode_cache,
+                                                       GFP_KERNEL);
+       if (!v9inode)
                return NULL;
-
-       vcookie->fscache = NULL;
-       vcookie->qid = NULL;
-       spin_lock_init(&vcookie->lock);
-       return &vcookie->inode;
+#ifdef CONFIG_9P_FSCACHE
+       v9inode->fscache = NULL;
+       spin_lock_init(&v9inode->fscache_lock);
+#endif
+       v9inode->writeback_fid = NULL;
+       v9inode->cache_validity = 0;
+       mutex_init(&v9inode->v_mutex);
+       return &v9inode->vfs_inode;
 }
 
 /**
@@ -237,71 +257,25 @@ struct inode *v9fs_alloc_inode(struct super_block *sb)
  *
  */
 
-void v9fs_destroy_inode(struct inode *inode)
-{
-       kmem_cache_free(vcookie_cache, v9fs_inode2cookie(inode));
-}
-#endif
-
-/**
- * v9fs_get_fsgid_for_create - Helper function to get the gid for creating a
- * new file system object. This checks the S_ISGID to determine the owning
- * group of the new file system object.
- */
-
-static gid_t v9fs_get_fsgid_for_create(struct inode *dir_inode)
+static void v9fs_i_callback(struct rcu_head *head)
 {
-       BUG_ON(dir_inode == NULL);
-
-       if (dir_inode->i_mode & S_ISGID) {
-               /* set_gid bit is set.*/
-               return dir_inode->i_gid;
-       }
-       return current_fsgid();
+       struct inode *inode = container_of(head, struct inode, i_rcu);
+       kmem_cache_free(v9fs_inode_cache, V9FS_I(inode));
 }
 
-/**
- * v9fs_dentry_from_dir_inode - helper function to get the dentry from
- * dir inode.
- *
- */
-
-static struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode)
+void v9fs_destroy_inode(struct inode *inode)
 {
-       struct dentry *dentry;
-
-       spin_lock(&dcache_lock);
-       /* Directory should have only one entry. */
-       BUG_ON(S_ISDIR(inode->i_mode) && !list_is_singular(&inode->i_dentry));
-       dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
-       spin_unlock(&dcache_lock);
-       return dentry;
+       call_rcu(&inode->i_rcu, v9fs_i_callback);
 }
 
-/**
- * v9fs_get_inode - helper function to setup an inode
- * @sb: superblock
- * @mode: mode to setup inode with
- *
- */
-
-struct inode *v9fs_get_inode(struct super_block *sb, int mode)
+int v9fs_init_inode(struct v9fs_session_info *v9ses,
+                   struct inode *inode, umode_t mode, dev_t rdev)
 {
-       int err;
-       struct inode *inode;
-       struct v9fs_session_info *v9ses = sb->s_fs_info;
-
-       P9_DPRINTK(P9_DEBUG_VFS, "super block: %p mode: %o\n", sb, mode);
-
-       inode = new_inode(sb);
-       if (!inode) {
-               P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n");
-               return ERR_PTR(-ENOMEM);
-       }
+       int err = 0;
 
        inode_init_owner(inode, NULL, mode);
        inode->i_blocks = 0;
-       inode->i_rdev = 0;
+       inode->i_rdev = rdev;
        inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
        inode->i_mapping->a_ops = &v9fs_addr_operations;
 
@@ -312,13 +286,11 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)
        case S_IFSOCK:
                if (v9fs_proto_dotl(v9ses)) {
                        inode->i_op = &v9fs_file_inode_operations_dotl;
-                       inode->i_fop = &v9fs_file_operations_dotl;
                } else if (v9fs_proto_dotu(v9ses)) {
                        inode->i_op = &v9fs_file_inode_operations;
-                       inode->i_fop = &v9fs_file_operations;
                } else {
-                       P9_DPRINTK(P9_DEBUG_ERROR,
-                                  "special files without extended mode\n");
+                       p9_debug(P9_DEBUG_ERROR,
+                                "special files without extended mode\n");
                        err = -EINVAL;
                        goto error;
                }
@@ -327,18 +299,24 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)
        case S_IFREG:
                if (v9fs_proto_dotl(v9ses)) {
                        inode->i_op = &v9fs_file_inode_operations_dotl;
-                       inode->i_fop = &v9fs_file_operations_dotl;
+                       if (v9ses->cache)
+                               inode->i_fop =
+                                       &v9fs_cached_file_operations_dotl;
+                       else
+                               inode->i_fop = &v9fs_file_operations_dotl;
                } else {
                        inode->i_op = &v9fs_file_inode_operations;
-                       inode->i_fop = &v9fs_file_operations;
+                       if (v9ses->cache)
+                               inode->i_fop = &v9fs_cached_file_operations;
+                       else
+                               inode->i_fop = &v9fs_file_operations;
                }
 
                break;
-
        case S_IFLNK:
                if (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)) {
-                       P9_DPRINTK(P9_DEBUG_ERROR, "extended modes used with "
-                                               "legacy protocol.\n");
+                       p9_debug(P9_DEBUG_ERROR,
+                                "extended modes used with legacy protocol\n");
                        err = -EINVAL;
                        goto error;
                }
@@ -365,17 +343,43 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)
 
                break;
        default:
-               P9_DPRINTK(P9_DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n",
-                          mode, mode & S_IFMT);
+               p9_debug(P9_DEBUG_ERROR, "BAD mode 0x%hx S_IFMT 0x%x\n",
+                        mode, mode & S_IFMT);
                err = -EINVAL;
                goto error;
        }
+error:
+       return err;
 
-       return inode;
+}
 
-error:
-       iput(inode);
-       return ERR_PTR(err);
+/**
+ * v9fs_get_inode - helper function to setup an inode
+ * @sb: superblock
+ * @mode: mode to setup inode with
+ *
+ */
+
+struct inode *v9fs_get_inode(struct super_block *sb, umode_t mode, dev_t rdev)
+{
+       int err;
+       struct inode *inode;
+       struct v9fs_session_info *v9ses = sb->s_fs_info;
+
+       p9_debug(P9_DEBUG_VFS, "super block: %p mode: %ho\n", sb, mode);
+
+       inode = new_inode(sb);
+       if (!inode) {
+               pr_warn("%s (%d): Problem allocating inode\n",
+                       __func__, task_pid_nr(current));
+               return ERR_PTR(-ENOMEM);
+       }
+       err = v9fs_init_inode(v9ses, inode, mode, rdev);
+       if (err) {
+               iput(inode);
+               return ERR_PTR(err);
+       }
+       return inode;
 }
 
 /*
@@ -438,139 +442,190 @@ error:
  */
 void v9fs_evict_inode(struct inode *inode)
 {
+       struct v9fs_inode *v9inode = V9FS_I(inode);
+
        truncate_inode_pages(inode->i_mapping, 0);
-       end_writeback(inode);
+       clear_inode(inode);
        filemap_fdatawrite(inode->i_mapping);
 
 #ifdef CONFIG_9P_FSCACHE
        v9fs_cache_inode_put_cookie(inode);
 #endif
+       /* clunk the fid stashed in writeback_fid */
+       if (v9inode->writeback_fid) {
+               p9_client_clunk(v9inode->writeback_fid);
+               v9inode->writeback_fid = NULL;
+       }
 }
 
-static struct inode *
-v9fs_inode(struct v9fs_session_info *v9ses, struct p9_fid *fid,
-       struct super_block *sb)
+static int v9fs_test_inode(struct inode *inode, void *data)
 {
-       int err, umode;
-       struct inode *ret = NULL;
-       struct p9_wstat *st;
+       int umode;
+       dev_t rdev;
+       struct v9fs_inode *v9inode = V9FS_I(inode);
+       struct p9_wstat *st = (struct p9_wstat *)data;
+       struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
+
+       umode = p9mode2unixmode(v9ses, st, &rdev);
+       /* don't match inode of different type */
+       if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
+               return 0;
+
+       /* compare qid details */
+       if (memcmp(&v9inode->qid.version,
+                  &st->qid.version, sizeof(v9inode->qid.version)))
+               return 0;
+
+       if (v9inode->qid.type != st->qid.type)
+               return 0;
+       return 1;
+}
 
-       st = p9_client_stat(fid);
-       if (IS_ERR(st))
-               return ERR_CAST(st);
+static int v9fs_test_new_inode(struct inode *inode, void *data)
+{
+       return 0;
+}
 
-       umode = p9mode2unixmode(v9ses, st->mode);
-       ret = v9fs_get_inode(sb, umode);
-       if (IS_ERR(ret)) {
-               err = PTR_ERR(ret);
-               goto error;
-       }
+static int v9fs_set_inode(struct inode *inode,  void *data)
+{
+       struct v9fs_inode *v9inode = V9FS_I(inode);
+       struct p9_wstat *st = (struct p9_wstat *)data;
+
+       memcpy(&v9inode->qid, &st->qid, sizeof(st->qid));
+       return 0;
+}
+
+static struct inode *v9fs_qid_iget(struct super_block *sb,
+                                  struct p9_qid *qid,
+                                  struct p9_wstat *st,
+                                  int new)
+{
+       dev_t rdev;
+       int retval;
+       umode_t umode;
+       unsigned long i_ino;
+       struct inode *inode;
+       struct v9fs_session_info *v9ses = sb->s_fs_info;
+       int (*test)(struct inode *, void *);
 
-       v9fs_stat2inode(st, ret, sb);
-       ret->i_ino = v9fs_qid2ino(&st->qid);
+       if (new)
+               test = v9fs_test_new_inode;
+       else
+               test = v9fs_test_inode;
+
+       i_ino = v9fs_qid2ino(qid);
+       inode = iget5_locked(sb, i_ino, test, v9fs_set_inode, st);
+       if (!inode)
+               return ERR_PTR(-ENOMEM);
+       if (!(inode->i_state & I_NEW))
+               return inode;
+       /*
+        * initialize the inode with the stat info
+        * FIXME!! we may need support for stale inodes
+        * later.
+        */
+       inode->i_ino = i_ino;
+       umode = p9mode2unixmode(v9ses, st, &rdev);
+       retval = v9fs_init_inode(v9ses, inode, umode, rdev);
+       if (retval)
+               goto error;
 
+       v9fs_stat2inode(st, inode, sb);
 #ifdef CONFIG_9P_FSCACHE
-       v9fs_vcookie_set_qid(ret, &st->qid);
-       v9fs_cache_inode_get_cookie(ret);
+       v9fs_cache_inode_get_cookie(inode);
 #endif
-       p9stat_free(st);
-       kfree(st);
-       return ret;
+       unlock_new_inode(inode);
+       return inode;
 error:
-       p9stat_free(st);
-       kfree(st);
-       return ERR_PTR(err);
+       unlock_new_inode(inode);
+       iput(inode);
+       return ERR_PTR(retval);
+
 }
 
-static struct inode *
-v9fs_inode_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid,
-       struct super_block *sb)
+struct inode *
+v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
+                   struct super_block *sb, int new)
 {
-       struct inode *ret = NULL;
-       int err;
-       struct p9_stat_dotl *st;
+       struct p9_wstat *st;
+       struct inode *inode = NULL;
 
-       st = p9_client_getattr_dotl(fid, P9_STATS_BASIC);
+       st = p9_client_stat(fid);
        if (IS_ERR(st))
                return ERR_CAST(st);
 
-       ret = v9fs_get_inode(sb, st->st_mode);
-       if (IS_ERR(ret)) {
-               err = PTR_ERR(ret);
-               goto error;
-       }
-
-       v9fs_stat2inode_dotl(st, ret);
-       ret->i_ino = v9fs_qid2ino(&st->qid);
-#ifdef CONFIG_9P_FSCACHE
-       v9fs_vcookie_set_qid(ret, &st->qid);
-       v9fs_cache_inode_get_cookie(ret);
-#endif
-       err = v9fs_get_acl(ret, fid);
-       if (err) {
-               iput(ret);
-               goto error;
-       }
-       kfree(st);
-       return ret;
-error:
+       inode = v9fs_qid_iget(sb, &st->qid, st, new);
+       p9stat_free(st);
        kfree(st);
-       return ERR_PTR(err);
+       return inode;
 }
 
 /**
- * v9fs_inode_from_fid - Helper routine to populate an inode by
- * issuing a attribute request
- * @v9ses: session information
- * @fid: fid to issue attribute request for
- * @sb: superblock on which to create inode
- *
+ * v9fs_at_to_dotl_flags- convert Linux specific AT flags to
+ * plan 9 AT flag.
+ * @flags: flags to convert
  */
-static inline struct inode *
-v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
-                       struct super_block *sb)
+static int v9fs_at_to_dotl_flags(int flags)
 {
-       if (v9fs_proto_dotl(v9ses))
-               return v9fs_inode_dotl(v9ses, fid, sb);
-       else
-               return v9fs_inode(v9ses, fid, sb);
+       int rflags = 0;
+       if (flags & AT_REMOVEDIR)
+               rflags |= P9_DOTL_AT_REMOVEDIR;
+       return rflags;
 }
 
 /**
  * v9fs_remove - helper function to remove files and directories
  * @dir: directory inode that is being deleted
- * @file:  dentry that is being deleted
+ * @dentry:  dentry that is being deleted
  * @rmdir: removing a directory
  *
  */
 
-static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
+static int v9fs_remove(struct inode *dir, struct dentry *dentry, int flags)
 {
-       int retval;
-       struct inode *file_inode;
-       struct p9_fid *v9fid;
+       struct inode *inode;
+       int retval = -EOPNOTSUPP;
+       struct p9_fid *v9fid, *dfid;
+       struct v9fs_session_info *v9ses;
 
-       P9_DPRINTK(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file,
-               rmdir);
+       p9_debug(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %x\n",
+                dir, dentry, flags);
 
-       file_inode = file->d_inode;
-       v9fid = v9fs_fid_clone(file);
-       if (IS_ERR(v9fid))
-               return PTR_ERR(v9fid);
+       v9ses = v9fs_inode2v9ses(dir);
+       inode = dentry->d_inode;
+       dfid = v9fs_fid_lookup(dentry->d_parent);
+       if (IS_ERR(dfid)) {
+               retval = PTR_ERR(dfid);
+               p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", retval);
+               return retval;
+       }
+       if (v9fs_proto_dotl(v9ses))
+               retval = p9_client_unlinkat(dfid, dentry->d_name.name,
+                                           v9fs_at_to_dotl_flags(flags));
+       if (retval == -EOPNOTSUPP) {
+               /* Try the one based on path */
+               v9fid = v9fs_fid_clone(dentry);
+               if (IS_ERR(v9fid))
+                       return PTR_ERR(v9fid);
+               retval = p9_client_remove(v9fid);
+       }
+       if (!retval) {
+               /*
+                * directories on unlink should have zero
+                * link count
+                */
+               if (flags & AT_REMOVEDIR) {
+                       clear_nlink(inode);
+                       drop_nlink(dir);
+               } else
+                       drop_nlink(inode);
 
-       retval = p9_client_remove(v9fid);
-       if (!retval)
-               drop_nlink(file_inode);
+               v9fs_invalidate_inode_attr(inode);
+               v9fs_invalidate_inode_attr(dir);
+       }
        return retval;
 }
 
-static int
-v9fs_open_created(struct inode *inode, struct file *file)
-{
-       return 0;
-}
-
-
 /**
  * v9fs_create - Create a file
  * @v9ses: session information
@@ -590,7 +645,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
        struct p9_fid *dfid, *ofid, *fid;
        struct inode *inode;
 
-       P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
+       p9_debug(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
 
        err = 0;
        ofid = NULL;
@@ -599,7 +654,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
        dfid = v9fs_fid_lookup(dentry->d_parent);
        if (IS_ERR(dfid)) {
                err = PTR_ERR(dfid);
-               P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
+               p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
                return ERR_PTR(err);
        }
 
@@ -607,250 +662,79 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
        ofid = p9_client_walk(dfid, 0, NULL, 1);
        if (IS_ERR(ofid)) {
                err = PTR_ERR(ofid);
-               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
+               p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
                return ERR_PTR(err);
        }
 
        err = p9_client_fcreate(ofid, name, perm, mode, extension);
        if (err < 0) {
-               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
-               goto error;
-       }
-
-       /* now walk from the parent so we can get unopened fid */
-       fid = p9_client_walk(dfid, 1, &name, 1);
-       if (IS_ERR(fid)) {
-               err = PTR_ERR(fid);
-               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
-               fid = NULL;
-               goto error;
-       }
-
-       /* instantiate inode and assign the unopened fid to the dentry */
-       inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
-       if (IS_ERR(inode)) {
-               err = PTR_ERR(inode);
-               P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
+               p9_debug(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
                goto error;
        }
 
-       if (v9ses->cache)
-               dentry->d_op = &v9fs_cached_dentry_operations;
-       else
-               dentry->d_op = &v9fs_dentry_operations;
-
-       d_instantiate(dentry, inode);
-       err = v9fs_fid_add(dentry, fid);
-       if (err < 0)
-               goto error;
-
-       return ofid;
-
-error:
-       if (ofid)
-               p9_client_clunk(ofid);
-
-       if (fid)
-               p9_client_clunk(fid);
-
-       return ERR_PTR(err);
-}
-
-/**
- * v9fs_vfs_create_dotl - VFS hook to create files for 9P2000.L protocol.
- * @dir: directory inode that is being created
- * @dentry:  dentry that is being deleted
- * @mode: create permissions
- * @nd: path information
- *
- */
-
-static int
-v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode,
-               struct nameidata *nd)
-{
-       int err = 0;
-       char *name = NULL;
-       gid_t gid;
-       int flags;
-       mode_t mode;
-       struct v9fs_session_info *v9ses;
-       struct p9_fid *fid = NULL;
-       struct p9_fid *dfid, *ofid;
-       struct file *filp;
-       struct p9_qid qid;
-       struct inode *inode;
-       struct posix_acl *pacl = NULL, *dacl = NULL;
-
-       v9ses = v9fs_inode2v9ses(dir);
-       if (nd && nd->flags & LOOKUP_OPEN)
-               flags = nd->intent.open.flags - 1;
-       else {
-               /*
-                * create call without LOOKUP_OPEN is due
-                * to mknod of regular files. So use mknod
-                * operation.
-                */
-               return v9fs_vfs_mknod_dotl(dir, dentry, omode, 0);
-       }
-
-       name = (char *) dentry->d_name.name;
-       P9_DPRINTK(P9_DEBUG_VFS, "v9fs_vfs_create_dotl: name:%s flags:0x%x "
-                       "mode:0x%x\n", name, flags, omode);
-
-       dfid = v9fs_fid_lookup(dentry->d_parent);
-       if (IS_ERR(dfid)) {
-               err = PTR_ERR(dfid);
-               P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
-               return err;
-       }
-
-       /* clone a fid to use for creation */
-       ofid = p9_client_walk(dfid, 0, NULL, 1);
-       if (IS_ERR(ofid)) {
-               err = PTR_ERR(ofid);
-               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
-               return err;
-       }
-
-       gid = v9fs_get_fsgid_for_create(dir);
-
-       mode = omode;
-       /* Update mode based on ACL value */
-       err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
-       if (err) {
-               P9_DPRINTK(P9_DEBUG_VFS,
-                          "Failed to get acl values in creat %d\n", err);
-               goto error;
-       }
-       err = p9_client_create_dotl(ofid, name, flags, mode, gid, &qid);
-       if (err < 0) {
-               P9_DPRINTK(P9_DEBUG_VFS,
-                               "p9_client_open_dotl failed in creat %d\n",
-                               err);
-               goto error;
-       }
-       /* instantiate inode and assign the unopened fid to the dentry */
-       if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE ||
-           (nd && nd->flags & LOOKUP_OPEN)) {
+       if (!(perm & P9_DMLINK)) {
+               /* now walk from the parent so we can get unopened fid */
                fid = p9_client_walk(dfid, 1, &name, 1);
                if (IS_ERR(fid)) {
                        err = PTR_ERR(fid);
-                       P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
-                               err);
+                       p9_debug(P9_DEBUG_VFS,
+                                  "p9_client_walk failed %d\n", err);
                        fid = NULL;
                        goto error;
                }
-
-               inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
-               if (IS_ERR(inode)) {
-                       err = PTR_ERR(inode);
-                       P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n",
-                               err);
-                       goto error;
-               }
-               dentry->d_op = &v9fs_cached_dentry_operations;
-               d_instantiate(dentry, inode);
-               err = v9fs_fid_add(dentry, fid);
-               if (err < 0)
-                       goto error;
-               /* The fid would get clunked via a dput */
-               fid = NULL;
-       } else {
                /*
-                * Not in cached mode. No need to populate
-                * inode with stat. We need to get an inode
-                * so that we can set the acl with dentry
+                * instantiate inode and assign the unopened fid to the dentry
                 */
-               inode = v9fs_get_inode(dir->i_sb, mode);
+               inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
                if (IS_ERR(inode)) {
                        err = PTR_ERR(inode);
+                       p9_debug(P9_DEBUG_VFS,
+                                  "inode creation failed %d\n", err);
                        goto error;
                }
-               dentry->d_op = &v9fs_dentry_operations;
+               v9fs_fid_add(dentry, fid);
                d_instantiate(dentry, inode);
        }
-       /* Now set the ACL based on the default value */
-       v9fs_set_create_acl(dentry, dacl, pacl);
-
-       /* if we are opening a file, assign the open fid to the file */
-       if (nd && nd->flags & LOOKUP_OPEN) {
-               filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created);
-               if (IS_ERR(filp)) {
-                       p9_client_clunk(ofid);
-                       return PTR_ERR(filp);
-               }
-               filp->private_data = ofid;
-       } else
-               p9_client_clunk(ofid);
-
-       return 0;
-
+       return ofid;
 error:
        if (ofid)
                p9_client_clunk(ofid);
+
        if (fid)
                p9_client_clunk(fid);
-       return err;
+
+       return ERR_PTR(err);
 }
 
 /**
- * v9fs_vfs_create - VFS hook to create files
+ * v9fs_vfs_create - VFS hook to create a regular file
+ *
+ * open(.., O_CREAT) is handled in v9fs_vfs_atomic_open().  This is only called
+ * for mknod(2).
+ *
  * @dir: directory inode that is being created
  * @dentry:  dentry that is being deleted
  * @mode: create permissions
- * @nd: path information
  *
  */
 
 static int
-v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
-               struct nameidata *nd)
+v9fs_vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+               bool excl)
 {
-       int err;
-       u32 perm;
-       int flags;
-       struct v9fs_session_info *v9ses;
+       struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
+       u32 perm = unixmode2p9mode(v9ses, mode);
        struct p9_fid *fid;
-       struct file *filp;
-
-       err = 0;
-       fid = NULL;
-       v9ses = v9fs_inode2v9ses(dir);
-       perm = unixmode2p9mode(v9ses, mode);
-       if (nd && nd->flags & LOOKUP_OPEN)
-               flags = nd->intent.open.flags - 1;
-       else
-               flags = O_RDWR;
-
-       fid = v9fs_create(v9ses, dir, dentry, NULL, perm,
-                               v9fs_uflags2omode(flags,
-                                               v9fs_proto_dotu(v9ses)));
-       if (IS_ERR(fid)) {
-               err = PTR_ERR(fid);
-               fid = NULL;
-               goto error;
-       }
 
-       /* if we are opening a file, assign the open fid to the file */
-       if (nd && nd->flags & LOOKUP_OPEN) {
-               filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created);
-               if (IS_ERR(filp)) {
-                       err = PTR_ERR(filp);
-                       goto error;
-               }
+       /* P9_OEXCL? */
+       fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_ORDWR);
+       if (IS_ERR(fid))
+               return PTR_ERR(fid);
 
-               filp->private_data = fid;
-       } else
-               p9_client_clunk(fid);
+       v9fs_invalidate_inode_attr(dir);
+       p9_client_clunk(fid);
 
        return 0;
-
-error:
-       if (fid)
-               p9_client_clunk(fid);
-
-       return err;
 }
 
 /**
@@ -861,14 +745,14 @@ error:
  *
  */
 
-static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
        int err;
        u32 perm;
-       struct v9fs_session_info *v9ses;
        struct p9_fid *fid;
+       struct v9fs_session_info *v9ses;
 
-       P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
+       p9_debug(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
        err = 0;
        v9ses = v9fs_inode2v9ses(dir);
        perm = unixmode2p9mode(v9ses, mode | S_IFDIR);
@@ -876,112 +760,14 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (IS_ERR(fid)) {
                err = PTR_ERR(fid);
                fid = NULL;
-       }
-
-       if (fid)
-               p9_client_clunk(fid);
-
-       return err;
-}
-
-
-/**
- * v9fs_vfs_mkdir_dotl - VFS mkdir hook to create a directory
- * @dir:  inode that is being unlinked
- * @dentry: dentry that is being unlinked
- * @mode: mode for new directory
- *
- */
-
-static int v9fs_vfs_mkdir_dotl(struct inode *dir,
-                              struct dentry *dentry, int omode)
-{
-       int err;
-       struct v9fs_session_info *v9ses;
-       struct p9_fid *fid = NULL, *dfid = NULL;
-       gid_t gid;
-       char *name;
-       mode_t mode;
-       struct inode *inode;
-       struct p9_qid qid;
-       struct dentry *dir_dentry;
-       struct posix_acl *dacl = NULL, *pacl = NULL;
-
-       P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
-       err = 0;
-       v9ses = v9fs_inode2v9ses(dir);
-
-       omode |= S_IFDIR;
-       if (dir->i_mode & S_ISGID)
-               omode |= S_ISGID;
-
-       dir_dentry = v9fs_dentry_from_dir_inode(dir);
-       dfid = v9fs_fid_lookup(dir_dentry);
-       if (IS_ERR(dfid)) {
-               err = PTR_ERR(dfid);
-               P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
-               dfid = NULL;
-               goto error;
-       }
-
-       gid = v9fs_get_fsgid_for_create(dir);
-       mode = omode;
-       /* Update mode based on ACL value */
-       err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
-       if (err) {
-               P9_DPRINTK(P9_DEBUG_VFS,
-                          "Failed to get acl values in mkdir %d\n", err);
-               goto error;
-       }
-       name = (char *) dentry->d_name.name;
-       err = p9_client_mkdir_dotl(dfid, name, mode, gid, &qid);
-       if (err < 0)
-               goto error;
-
-       /* instantiate inode and assign the unopened fid to the dentry */
-       if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
-               fid = p9_client_walk(dfid, 1, &name, 1);
-               if (IS_ERR(fid)) {
-                       err = PTR_ERR(fid);
-                       P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
-                               err);
-                       fid = NULL;
-                       goto error;
-               }
-
-               inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
-               if (IS_ERR(inode)) {
-                       err = PTR_ERR(inode);
-                       P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n",
-                               err);
-                       goto error;
-               }
-               dentry->d_op = &v9fs_cached_dentry_operations;
-               d_instantiate(dentry, inode);
-               err = v9fs_fid_add(dentry, fid);
-               if (err < 0)
-                       goto error;
-               fid = NULL;
        } else {
-               /*
-                * Not in cached mode. No need to populate
-                * inode with stat. We need to get an inode
-                * so that we can set the acl with dentry
-                */
-               inode = v9fs_get_inode(dir->i_sb, mode);
-               if (IS_ERR(inode)) {
-                       err = PTR_ERR(inode);
-                       goto error;
-               }
-               dentry->d_op = &v9fs_dentry_operations;
-               d_instantiate(dentry, inode);
+               inc_nlink(dir);
+               v9fs_invalidate_inode_attr(dir);
        }
-       /* Now set the ACL based on the default value */
-       v9fs_set_create_acl(dentry, dacl, pacl);
 
-error:
        if (fid)
                p9_client_clunk(fid);
+
        return err;
 }
 
@@ -993,18 +779,18 @@ error:
  *
  */
 
-static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
-                                     struct nameidata *nameidata)
+struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
+                                     unsigned int flags)
 {
+       struct dentry *res;
        struct super_block *sb;
        struct v9fs_session_info *v9ses;
        struct p9_fid *dfid, *fid;
        struct inode *inode;
        char *name;
-       int result = 0;
 
-       P9_DPRINTK(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n",
-               dir, dentry->d_name.name, dentry, nameidata);
+       p9_debug(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p flags: %x\n",
+                dir, dentry->d_name.name, dentry, flags);
 
        if (dentry->d_name.len > NAME_MAX)
                return ERR_PTR(-ENAMETOOLONG);
@@ -1019,39 +805,120 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
        name = (char *) dentry->d_name.name;
        fid = p9_client_walk(dfid, 1, &name, 1);
        if (IS_ERR(fid)) {
-               result = PTR_ERR(fid);
-               if (result == -ENOENT) {
-                       inode = NULL;
-                       goto inst_out;
+               if (fid == ERR_PTR(-ENOENT)) {
+                       d_add(dentry, NULL);
+                       return NULL;
                }
+               return ERR_CAST(fid);
+       }
+       /*
+        * Make sure we don't use a wrong inode due to parallel
+        * unlink. For cached mode create calls request for new
+        * inode. But with cache disabled, lookup should do this.
+        */
+       if (v9ses->cache)
+               inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
+       else
+               inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
+       if (IS_ERR(inode)) {
+               p9_client_clunk(fid);
+               return ERR_CAST(inode);
+       }
+       /*
+        * If we had a rename on the server and a parallel lookup
+        * for the new name, then make sure we instantiate with
+        * the new name. ie look up for a/b, while on server somebody
+        * moved b under k and client parallely did a lookup for
+        * k/b.
+        */
+       res = d_materialise_unique(dentry, inode);
+       if (!res)
+               v9fs_fid_add(dentry, fid);
+       else if (!IS_ERR(res))
+               v9fs_fid_add(res, fid);
+       else
+               p9_client_clunk(fid);
+       return res;
+}
 
-               return ERR_PTR(result);
+static int
+v9fs_vfs_atomic_open(struct inode *dir, struct dentry *dentry,
+                    struct file *file, unsigned flags, umode_t mode,
+                    int *opened)
+{
+       int err;
+       u32 perm;
+       struct v9fs_inode *v9inode;
+       struct v9fs_session_info *v9ses;
+       struct p9_fid *fid, *inode_fid;
+       struct dentry *res = NULL;
+
+       if (d_unhashed(dentry)) {
+               res = v9fs_vfs_lookup(dir, dentry, 0);
+               if (IS_ERR(res))
+                       return PTR_ERR(res);
+
+               if (res)
+                       dentry = res;
        }
 
-       inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
-       if (IS_ERR(inode)) {
-               result = PTR_ERR(inode);
-               inode = NULL;
+       /* Only creates */
+       if (!(flags & O_CREAT) || dentry->d_inode)
+               return finish_no_open(file, res);
+
+       err = 0;
+       fid = NULL;
+       v9ses = v9fs_inode2v9ses(dir);
+       perm = unixmode2p9mode(v9ses, mode);
+       fid = v9fs_create(v9ses, dir, dentry, NULL, perm,
+                               v9fs_uflags2omode(flags,
+                                               v9fs_proto_dotu(v9ses)));
+       if (IS_ERR(fid)) {
+               err = PTR_ERR(fid);
+               fid = NULL;
                goto error;
        }
 
-       result = v9fs_fid_add(dentry, fid);
-       if (result < 0)
+       v9fs_invalidate_inode_attr(dir);
+       v9inode = V9FS_I(dentry->d_inode);
+       mutex_lock(&v9inode->v_mutex);
+       if (v9ses->cache && !v9inode->writeback_fid &&
+           ((flags & O_ACCMODE) != O_RDONLY)) {
+               /*
+                * clone a fid and add it to writeback_fid
+                * we do it during open time instead of
+                * page dirty time via write_begin/page_mkwrite
+                * because we want write after unlink usecase
+                * to work.
+                */
+               inode_fid = v9fs_writeback_fid(dentry);
+               if (IS_ERR(inode_fid)) {
+                       err = PTR_ERR(inode_fid);
+                       mutex_unlock(&v9inode->v_mutex);
+                       goto error;
+               }
+               v9inode->writeback_fid = (void *) inode_fid;
+       }
+       mutex_unlock(&v9inode->v_mutex);
+       err = finish_open(file, dentry, generic_file_open, opened);
+       if (err)
                goto error;
 
-inst_out:
+       file->private_data = fid;
+#ifdef CONFIG_9P_FSCACHE
        if (v9ses->cache)
-               dentry->d_op = &v9fs_cached_dentry_operations;
-       else
-               dentry->d_op = &v9fs_dentry_operations;
+               v9fs_cache_inode_set_cookie(dentry->d_inode, file);
+#endif
 
-       d_add(dentry, inode);
-       return NULL;
+       *opened |= FILE_CREATED;
+out:
+       dput(res);
+       return err;
 
 error:
-       p9_client_clunk(fid);
-
-       return ERR_PTR(result);
+       if (fid)
+               p9_client_clunk(fid);
+       goto out;
 }
 
 /**
@@ -1061,7 +928,7 @@ error:
  *
  */
 
-static int v9fs_vfs_unlink(struct inode *i, struct dentry *d)
+int v9fs_vfs_unlink(struct inode *i, struct dentry *d)
 {
        return v9fs_remove(i, d, 0);
 }
@@ -1073,9 +940,9 @@ static int v9fs_vfs_unlink(struct inode *i, struct dentry *d)
  *
  */
 
-static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
+int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
 {
-       return v9fs_remove(i, d, 1);
+       return v9fs_remove(i, d, AT_REMOVEDIR);
 }
 
 /**
@@ -1087,21 +954,23 @@ static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
  *
  */
 
-static int
+int
 v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                struct inode *new_dir, struct dentry *new_dentry)
 {
+       int retval;
        struct inode *old_inode;
+       struct inode *new_inode;
        struct v9fs_session_info *v9ses;
        struct p9_fid *oldfid;
        struct p9_fid *olddirfid;
        struct p9_fid *newdirfid;
        struct p9_wstat wstat;
-       int retval;
 
-       P9_DPRINTK(P9_DEBUG_VFS, "\n");
+       p9_debug(P9_DEBUG_VFS, "\n");
        retval = 0;
        old_inode = old_dentry->d_inode;
+       new_inode = new_dentry->d_inode;
        v9ses = v9fs_inode2v9ses(old_inode);
        oldfid = v9fs_fid_lookup(old_dentry);
        if (IS_ERR(oldfid))
@@ -1121,9 +990,12 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 
        down_write(&v9ses->rename_sem);
        if (v9fs_proto_dotl(v9ses)) {
-               retval = p9_client_rename(oldfid, newdirfid,
-                                       (char *) new_dentry->d_name.name);
-               if (retval != -ENOSYS)
+               retval = p9_client_renameat(olddirfid, old_dentry->d_name.name,
+                                           newdirfid, new_dentry->d_name.name);
+               if (retval == -EOPNOTSUPP)
+                       retval = p9_client_rename(oldfid, newdirfid,
+                                                 new_dentry->d_name.name);
+               if (retval != -EOPNOTSUPP)
                        goto clunk_newdir;
        }
        if (old_dentry->d_parent != new_dentry->d_parent) {
@@ -1131,8 +1003,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                 * 9P .u can only handle file rename in the same directory
                 */
 
-               P9_DPRINTK(P9_DEBUG_ERROR,
-                               "old dir and new dir are different\n");
+               p9_debug(P9_DEBUG_ERROR, "old dir and new dir are different\n");
                retval = -EXDEV;
                goto clunk_newdir;
        }
@@ -1142,9 +1013,25 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        retval = p9_client_wstat(oldfid, &wstat);
 
 clunk_newdir:
-       if (!retval)
+       if (!retval) {
+               if (new_inode) {
+                       if (S_ISDIR(new_inode->i_mode))
+                               clear_nlink(new_inode);
+                       else
+                               drop_nlink(new_inode);
+               }
+               if (S_ISDIR(old_inode->i_mode)) {
+                       if (!new_inode)
+                               inc_nlink(new_dir);
+                       drop_nlink(old_dir);
+               }
+               v9fs_invalidate_inode_attr(old_inode);
+               v9fs_invalidate_inode_attr(old_dir);
+               v9fs_invalidate_inode_attr(new_dir);
+
                /* successful rename */
                d_move(old_dentry, new_dentry);
+       }
        up_write(&v9ses->rename_sem);
        p9_client_clunk(newdirfid);
 
@@ -1172,12 +1059,13 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
        struct p9_fid *fid;
        struct p9_wstat *st;
 
-       P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
+       p9_debug(P9_DEBUG_VFS, "dentry: %p\n", dentry);
        err = -EPERM;
-       v9ses = v9fs_inode2v9ses(dentry->d_inode);
-       if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
-               return simple_getattr(mnt, dentry, stat);
-
+       v9ses = v9fs_dentry2v9ses(dentry);
+       if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
+               generic_fillattr(dentry->d_inode, stat);
+               return 0;
+       }
        fid = v9fs_fid_lookup(dentry);
        if (IS_ERR(fid))
                return PTR_ERR(fid);
@@ -1187,45 +1075,9 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
                return PTR_ERR(st);
 
        v9fs_stat2inode(st, dentry->d_inode, dentry->d_inode->i_sb);
-               generic_fillattr(dentry->d_inode, stat);
-
-       p9stat_free(st);
-       kfree(st);
-       return 0;
-}
-
-static int
-v9fs_vfs_getattr_dotl(struct vfsmount *mnt, struct dentry *dentry,
-                struct kstat *stat)
-{
-       int err;
-       struct v9fs_session_info *v9ses;
-       struct p9_fid *fid;
-       struct p9_stat_dotl *st;
-
-       P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
-       err = -EPERM;
-       v9ses = v9fs_inode2v9ses(dentry->d_inode);
-       if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
-               return simple_getattr(mnt, dentry, stat);
-
-       fid = v9fs_fid_lookup(dentry);
-       if (IS_ERR(fid))
-               return PTR_ERR(fid);
-
-       /* Ask for all the fields in stat structure. Server will return
-        * whatever it supports
-        */
-
-       st = p9_client_getattr_dotl(fid, P9_STATS_ALL);
-       if (IS_ERR(st))
-               return PTR_ERR(st);
-
-       v9fs_stat2inode_dotl(st, dentry->d_inode);
        generic_fillattr(dentry->d_inode, stat);
-       /* Change block size to what the server returned */
-       stat->blksize = st->st_blksize;
 
+       p9stat_free(st);
        kfree(st);
        return 0;
 }
@@ -1244,9 +1096,13 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
        struct p9_fid *fid;
        struct p9_wstat wstat;
 
-       P9_DPRINTK(P9_DEBUG_VFS, "\n");
+       p9_debug(P9_DEBUG_VFS, "\n");
+       retval = inode_change_ok(dentry->d_inode, iattr);
+       if (retval)
+               return retval;
+
        retval = -EPERM;
-       v9ses = v9fs_inode2v9ses(dentry->d_inode);
+       v9ses = v9fs_dentry2v9ses(dentry);
        fid = v9fs_fid_lookup(dentry);
        if(IS_ERR(fid))
                return PTR_ERR(fid);
@@ -1272,77 +1128,22 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
                        wstat.n_gid = iattr->ia_gid;
        }
 
+       /* Write all dirty data */
+       if (S_ISREG(dentry->d_inode->i_mode))
+               filemap_write_and_wait(dentry->d_inode->i_mapping);
+
        retval = p9_client_wstat(fid, &wstat);
        if (retval < 0)
                return retval;
 
        if ((iattr->ia_valid & ATTR_SIZE) &&
-           iattr->ia_size != i_size_read(dentry->d_inode)) {
-               retval = vmtruncate(dentry->d_inode, iattr->ia_size);
-               if (retval)
-                       return retval;
-       }
-
-       setattr_copy(dentry->d_inode, iattr);
-       mark_inode_dirty(dentry->d_inode);
-       return 0;
-}
-
-/**
- * v9fs_vfs_setattr_dotl - set file metadata
- * @dentry: file whose metadata to set
- * @iattr: metadata assignment structure
- *
- */
-
-int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
-{
-       int retval;
-       struct v9fs_session_info *v9ses;
-       struct p9_fid *fid;
-       struct p9_iattr_dotl p9attr;
-
-       P9_DPRINTK(P9_DEBUG_VFS, "\n");
-
-       retval = inode_change_ok(dentry->d_inode, iattr);
-       if (retval)
-               return retval;
-
-       p9attr.valid = iattr->ia_valid;
-       p9attr.mode = iattr->ia_mode;
-       p9attr.uid = iattr->ia_uid;
-       p9attr.gid = iattr->ia_gid;
-       p9attr.size = iattr->ia_size;
-       p9attr.atime_sec = iattr->ia_atime.tv_sec;
-       p9attr.atime_nsec = iattr->ia_atime.tv_nsec;
-       p9attr.mtime_sec = iattr->ia_mtime.tv_sec;
-       p9attr.mtime_nsec = iattr->ia_mtime.tv_nsec;
-
-       retval = -EPERM;
-       v9ses = v9fs_inode2v9ses(dentry->d_inode);
-       fid = v9fs_fid_lookup(dentry);
-       if (IS_ERR(fid))
-               return PTR_ERR(fid);
-
-       retval = p9_client_setattr(fid, &p9attr);
-       if (retval < 0)
-               return retval;
+           iattr->ia_size != i_size_read(dentry->d_inode))
+               truncate_setsize(dentry->d_inode, iattr->ia_size);
 
-       if ((iattr->ia_valid & ATTR_SIZE) &&
-           iattr->ia_size != i_size_read(dentry->d_inode)) {
-               retval = vmtruncate(dentry->d_inode, iattr->ia_size);
-               if (retval)
-                       return retval;
-       }
+       v9fs_invalidate_inode_attr(dentry->d_inode);
 
        setattr_copy(dentry->d_inode, iattr);
        mark_inode_dirty(dentry->d_inode);
-       if (iattr->ia_valid & ATTR_MODE) {
-               /* We also want to update ACL when we update mode bits */
-               retval = v9fs_acl_chmod(dentry);
-               if (retval < 0)
-                       return retval;
-       }
        return 0;
 }
 
@@ -1358,12 +1159,14 @@ void
 v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
        struct super_block *sb)
 {
+       umode_t mode;
        char ext[32];
        char tag_name[14];
        unsigned int i_nlink;
        struct v9fs_session_info *v9ses = sb->s_fs_info;
+       struct v9fs_inode *v9inode = V9FS_I(inode);
 
-       inode->i_nlink = 1;
+       set_nlink(inode, 1);
 
        inode->i_atime.tv_sec = stat->atime;
        inode->i_mtime.tv_sec = stat->mtime;
@@ -1389,109 +1192,17 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
                        /* HARDLINKCOUNT %u */
                        sscanf(ext, "%13s %u", tag_name, &i_nlink);
                        if (!strncmp(tag_name, "HARDLINKCOUNT", 13))
-                               inode->i_nlink = i_nlink;
+                               set_nlink(inode, i_nlink);
                }
        }
-       inode->i_mode = p9mode2unixmode(v9ses, stat->mode);
-       if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) {
-               char type = 0;
-               int major = -1;
-               int minor = -1;
-
-               strncpy(ext, stat->extension, sizeof(ext));
-               sscanf(ext, "%c %u %u", &type, &major, &minor);
-               switch (type) {
-               case 'c':
-                       inode->i_mode &= ~S_IFBLK;
-                       inode->i_mode |= S_IFCHR;
-                       break;
-               case 'b':
-                       break;
-               default:
-                       P9_DPRINTK(P9_DEBUG_ERROR,
-                               "Unknown special type %c %s\n", type,
-                               stat->extension);
-               };
-               inode->i_rdev = MKDEV(major, minor);
-               init_special_inode(inode, inode->i_mode, inode->i_rdev);
-       } else
-               inode->i_rdev = 0;
-
+       mode = p9mode2perm(v9ses, stat);
+       mode |= inode->i_mode & ~S_IALLUGO;
+       inode->i_mode = mode;
        i_size_write(inode, stat->length);
 
        /* not real number of blocks, but 512 byte ones ... */
        inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9;
-}
-
-/**
- * v9fs_stat2inode_dotl - populate an inode structure with stat info
- * @stat: stat structure
- * @inode: inode to populate
- * @sb: superblock of filesystem
- *
- */
-
-void
-v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode)
-{
-
-       if ((stat->st_result_mask & P9_STATS_BASIC) == P9_STATS_BASIC) {
-               inode->i_atime.tv_sec = stat->st_atime_sec;
-               inode->i_atime.tv_nsec = stat->st_atime_nsec;
-               inode->i_mtime.tv_sec = stat->st_mtime_sec;
-               inode->i_mtime.tv_nsec = stat->st_mtime_nsec;
-               inode->i_ctime.tv_sec = stat->st_ctime_sec;
-               inode->i_ctime.tv_nsec = stat->st_ctime_nsec;
-               inode->i_uid = stat->st_uid;
-               inode->i_gid = stat->st_gid;
-               inode->i_nlink = stat->st_nlink;
-               inode->i_mode = stat->st_mode;
-               inode->i_rdev = new_decode_dev(stat->st_rdev);
-
-               if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode)))
-                       init_special_inode(inode, inode->i_mode, inode->i_rdev);
-
-               i_size_write(inode, stat->st_size);
-               inode->i_blocks = stat->st_blocks;
-       } else {
-               if (stat->st_result_mask & P9_STATS_ATIME) {
-                       inode->i_atime.tv_sec = stat->st_atime_sec;
-                       inode->i_atime.tv_nsec = stat->st_atime_nsec;
-               }
-               if (stat->st_result_mask & P9_STATS_MTIME) {
-                       inode->i_mtime.tv_sec = stat->st_mtime_sec;
-                       inode->i_mtime.tv_nsec = stat->st_mtime_nsec;
-               }
-               if (stat->st_result_mask & P9_STATS_CTIME) {
-                       inode->i_ctime.tv_sec = stat->st_ctime_sec;
-                       inode->i_ctime.tv_nsec = stat->st_ctime_nsec;
-               }
-               if (stat->st_result_mask & P9_STATS_UID)
-                       inode->i_uid = stat->st_uid;
-               if (stat->st_result_mask & P9_STATS_GID)
-                       inode->i_gid = stat->st_gid;
-               if (stat->st_result_mask & P9_STATS_NLINK)
-                       inode->i_nlink = stat->st_nlink;
-               if (stat->st_result_mask & P9_STATS_MODE) {
-                       inode->i_mode = stat->st_mode;
-                       if ((S_ISBLK(inode->i_mode)) ||
-                                               (S_ISCHR(inode->i_mode)))
-                               init_special_inode(inode, inode->i_mode,
-                                                               inode->i_rdev);
-               }
-               if (stat->st_result_mask & P9_STATS_RDEV)
-                       inode->i_rdev = new_decode_dev(stat->st_rdev);
-               if (stat->st_result_mask & P9_STATS_SIZE)
-                       i_size_write(inode, stat->st_size);
-               if (stat->st_result_mask & P9_STATS_BLOCKS)
-                       inode->i_blocks = stat->st_blocks;
-       }
-       if (stat->st_result_mask & P9_STATS_GEN)
-                       inode->i_generation = stat->st_gen;
-
-       /* Currently we don't support P9_STATS_BTIME and P9_STATS_DATA_VERSION
-        * because the inode structure does not have fields for them.
-        */
+       v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;
 }
 
 /**
@@ -1530,9 +1241,9 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
        struct p9_fid *fid;
        struct p9_wstat *st;
 
-       P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
+       p9_debug(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
        retval = -EPERM;
-       v9ses = v9fs_inode2v9ses(dentry->d_inode);
+       v9ses = v9fs_dentry2v9ses(dentry);
        fid = v9fs_fid_lookup(dentry);
        if (IS_ERR(fid))
                return PTR_ERR(fid);
@@ -1550,12 +1261,12 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
        }
 
        /* copy extension buffer into buffer */
-       strncpy(buffer, st->extension, buflen);
+       retval = min(strlen(st->extension)+1, (size_t)buflen);
+       memcpy(buffer, st->extension, retval);
 
-       P9_DPRINTK(P9_DEBUG_VFS,
-               "%s -> %s (%s)\n", dentry->d_name.name, st->extension, buffer);
+       p9_debug(P9_DEBUG_VFS, "%s -> %s (%.*s)\n",
+                dentry->d_name.name, st->extension, buflen, buffer);
 
-       retval = strnlen(buffer, buflen);
 done:
        p9stat_free(st);
        kfree(st);
@@ -1574,7 +1285,7 @@ static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
        int len = 0;
        char *link = __getname();
 
-       P9_DPRINTK(P9_DEBUG_VFS, "%s n", dentry->d_name.name);
+       p9_debug(P9_DEBUG_VFS, "%s\n", dentry->d_name.name);
 
        if (!link)
                link = ERR_PTR(-ENOMEM);
@@ -1600,13 +1311,13 @@ static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
  *
  */
 
-static void
+void
 v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
 {
        char *s = nd_get_link(nd);
 
-       P9_DPRINTK(P9_DEBUG_VFS, " %s %s\n", dentry->d_name.name,
-               IS_ERR(s) ? "<error>" : s);
+       p9_debug(P9_DEBUG_VFS, " %s %s\n",
+                dentry->d_name.name, IS_ERR(s) ? "<error>" : s);
        if (!IS_ERR(s))
                __putname(s);
 }
@@ -1621,117 +1332,28 @@ v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
  */
 
 static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
-       int mode, const char *extension)
+       u32 perm, const char *extension)
 {
-       u32 perm;
-       struct v9fs_session_info *v9ses;
        struct p9_fid *fid;
+       struct v9fs_session_info *v9ses;
 
        v9ses = v9fs_inode2v9ses(dir);
        if (!v9fs_proto_dotu(v9ses)) {
-               P9_DPRINTK(P9_DEBUG_ERROR, "not extended\n");
+               p9_debug(P9_DEBUG_ERROR, "not extended\n");
                return -EPERM;
        }
 
-       perm = unixmode2p9mode(v9ses, mode);
        fid = v9fs_create(v9ses, dir, dentry, (char *) extension, perm,
                                                                P9_OREAD);
        if (IS_ERR(fid))
                return PTR_ERR(fid);
 
+       v9fs_invalidate_inode_attr(dir);
        p9_client_clunk(fid);
        return 0;
 }
 
 /**
- * v9fs_vfs_symlink_dotl - helper function to create symlinks
- * @dir: directory inode containing symlink
- * @dentry: dentry for symlink
- * @symname: symlink data
- *
- * See Also: 9P2000.L RFC for more information
- *
- */
-
-static int
-v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
-               const char *symname)
-{
-       struct v9fs_session_info *v9ses;
-       struct p9_fid *dfid;
-       struct p9_fid *fid = NULL;
-       struct inode *inode;
-       struct p9_qid qid;
-       char *name;
-       int err;
-       gid_t gid;
-
-       name = (char *) dentry->d_name.name;
-       P9_DPRINTK(P9_DEBUG_VFS, "v9fs_vfs_symlink_dotl : %lu,%s,%s\n",
-                       dir->i_ino, name, symname);
-       v9ses = v9fs_inode2v9ses(dir);
-
-       dfid = v9fs_fid_lookup(dentry->d_parent);
-       if (IS_ERR(dfid)) {
-               err = PTR_ERR(dfid);
-               P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
-               return err;
-       }
-
-       gid = v9fs_get_fsgid_for_create(dir);
-
-       /* Server doesn't alter fid on TSYMLINK. Hence no need to clone it. */
-       err = p9_client_symlink(dfid, name, (char *)symname, gid, &qid);
-
-       if (err < 0) {
-               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_symlink failed %d\n", err);
-               goto error;
-       }
-
-       if (v9ses->cache) {
-               /* Now walk from the parent so we can get an unopened fid. */
-               fid = p9_client_walk(dfid, 1, &name, 1);
-               if (IS_ERR(fid)) {
-                       err = PTR_ERR(fid);
-                       P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
-                                       err);
-                       fid = NULL;
-                       goto error;
-               }
-
-               /* instantiate inode and assign the unopened fid to dentry */
-               inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
-               if (IS_ERR(inode)) {
-                       err = PTR_ERR(inode);
-                       P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n",
-                                       err);
-                       goto error;
-               }
-               dentry->d_op = &v9fs_cached_dentry_operations;
-               d_instantiate(dentry, inode);
-               err = v9fs_fid_add(dentry, fid);
-               if (err < 0)
-                       goto error;
-               fid = NULL;
-       } else {
-               /* Not in cached mode. No need to populate inode with stat */
-               inode = v9fs_get_inode(dir->i_sb, S_IFLNK);
-               if (IS_ERR(inode)) {
-                       err = PTR_ERR(inode);
-                       goto error;
-               }
-               dentry->d_op = &v9fs_dentry_operations;
-               d_instantiate(dentry, inode);
-       }
-
-error:
-       if (fid)
-               p9_client_clunk(fid);
-
-       return err;
-}
-
-/**
  * v9fs_vfs_symlink - helper function to create symlinks
  * @dir: directory inode containing symlink
  * @dentry: dentry for symlink
@@ -1744,10 +1366,10 @@ error:
 static int
 v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 {
-       P9_DPRINTK(P9_DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino,
-                                       dentry->d_name.name, symname);
+       p9_debug(P9_DEBUG_VFS, " %lu,%s,%s\n",
+                dir->i_ino, dentry->d_name.name, symname);
 
-       return v9fs_vfs_mkspecial(dir, dentry, S_IFLNK, symname);
+       return v9fs_vfs_mkspecial(dir, dentry, P9_DMSYMLINK, symname);
 }
 
 /**
@@ -1763,12 +1385,11 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
              struct dentry *dentry)
 {
        int retval;
-       struct p9_fid *oldfid;
        char *name;
+       struct p9_fid *oldfid;
 
-       P9_DPRINTK(P9_DEBUG_VFS,
-               " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
-               old_dentry->d_name.name);
+       p9_debug(P9_DEBUG_VFS, " %lu,%s,%s\n",
+                dir->i_ino, dentry->d_name.name, old_dentry->d_name.name);
 
        oldfid = v9fs_fid_clone(old_dentry);
        if (IS_ERR(oldfid))
@@ -1783,84 +1404,16 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
        sprintf(name, "%d\n", oldfid->fid);
        retval = v9fs_vfs_mkspecial(dir, dentry, P9_DMLINK, name);
        __putname(name);
-
+       if (!retval) {
+               v9fs_refresh_inode(oldfid, old_dentry->d_inode);
+               v9fs_invalidate_inode_attr(dir);
+       }
 clunk_fid:
        p9_client_clunk(oldfid);
        return retval;
 }
 
 /**
- * v9fs_vfs_link_dotl - create a hardlink for dotl
- * @old_dentry: dentry for file to link to
- * @dir: inode destination for new link
- * @dentry: dentry for link
- *
- */
-
-static int
-v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
-               struct dentry *dentry)
-{
-       int err;
-       struct p9_fid *dfid, *oldfid;
-       char *name;
-       struct v9fs_session_info *v9ses;
-       struct dentry *dir_dentry;
-
-       P9_DPRINTK(P9_DEBUG_VFS, "dir ino: %lu, old_name: %s, new_name: %s\n",
-                       dir->i_ino, old_dentry->d_name.name,
-                       dentry->d_name.name);
-
-       v9ses = v9fs_inode2v9ses(dir);
-       dir_dentry = v9fs_dentry_from_dir_inode(dir);
-       dfid = v9fs_fid_lookup(dir_dentry);
-       if (IS_ERR(dfid))
-               return PTR_ERR(dfid);
-
-       oldfid = v9fs_fid_lookup(old_dentry);
-       if (IS_ERR(oldfid))
-               return PTR_ERR(oldfid);
-
-       name = (char *) dentry->d_name.name;
-
-       err = p9_client_link(dfid, oldfid, (char *)dentry->d_name.name);
-
-       if (err < 0) {
-               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_link failed %d\n", err);
-               return err;
-       }
-
-       if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
-               /* Get the latest stat info from server. */
-               struct p9_fid *fid;
-               struct p9_stat_dotl *st;
-
-               fid = v9fs_fid_lookup(old_dentry);
-               if (IS_ERR(fid))
-                       return PTR_ERR(fid);
-
-               st = p9_client_getattr_dotl(fid, P9_STATS_BASIC);
-               if (IS_ERR(st))
-                       return PTR_ERR(st);
-
-               v9fs_stat2inode_dotl(st, old_dentry->d_inode);
-
-               kfree(st);
-       } else {
-               /* Caching disabled. No need to get upto date stat info.
-                * This dentry will be released immediately. So, just hold the
-                * inode
-                */
-               ihold(old_dentry->d_inode);
-       }
-
-       dentry->d_op = old_dentry->d_op;
-       d_instantiate(dentry, old_dentry->d_inode);
-
-       return err;
-}
-
-/**
  * v9fs_vfs_mknod - create a special file
  * @dir: inode destination for new link
  * @dentry: dentry for file
@@ -1870,14 +1423,16 @@ v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
  */
 
 static int
-v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
+v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev)
 {
+       struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
        int retval;
        char *name;
+       u32 perm;
 
-       P9_DPRINTK(P9_DEBUG_VFS,
-               " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino,
-               dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev));
+       p9_debug(P9_DEBUG_VFS, " %lu,%s mode: %hx MAJOR: %u MINOR: %u\n",
+                dir->i_ino, dentry->d_name.name, mode,
+                MAJOR(rdev), MINOR(rdev));
 
        if (!new_valid_dev(rdev))
                return -EINVAL;
@@ -1899,169 +1454,52 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
                return -EINVAL;
        }
 
-       retval = v9fs_vfs_mkspecial(dir, dentry, mode, name);
+       perm = unixmode2p9mode(v9ses, mode);
+       retval = v9fs_vfs_mkspecial(dir, dentry, perm, name);
        __putname(name);
 
        return retval;
 }
 
-/**
- * v9fs_vfs_mknod_dotl - create a special file
- * @dir: inode destination for new link
- * @dentry: dentry for file
- * @mode: mode for creation
- * @rdev: device associated with special file
- *
- */
-static int
-v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
-               dev_t rdev)
+int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
 {
-       int err;
-       char *name;
-       mode_t mode;
+       int umode;
+       dev_t rdev;
+       loff_t i_size;
+       struct p9_wstat *st;
        struct v9fs_session_info *v9ses;
-       struct p9_fid *fid = NULL, *dfid = NULL;
-       struct inode *inode;
-       gid_t gid;
-       struct p9_qid qid;
-       struct dentry *dir_dentry;
-       struct posix_acl *dacl = NULL, *pacl = NULL;
-
-       P9_DPRINTK(P9_DEBUG_VFS,
-               " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino,
-               dentry->d_name.name, omode, MAJOR(rdev), MINOR(rdev));
-
-       if (!new_valid_dev(rdev))
-               return -EINVAL;
-
-       v9ses = v9fs_inode2v9ses(dir);
-       dir_dentry = v9fs_dentry_from_dir_inode(dir);
-       dfid = v9fs_fid_lookup(dir_dentry);
-       if (IS_ERR(dfid)) {
-               err = PTR_ERR(dfid);
-               P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
-               dfid = NULL;
-               goto error;
-       }
-
-       gid = v9fs_get_fsgid_for_create(dir);
-       mode = omode;
-       /* Update mode based on ACL value */
-       err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
-       if (err) {
-               P9_DPRINTK(P9_DEBUG_VFS,
-                          "Failed to get acl values in mknod %d\n", err);
-               goto error;
-       }
-       name = (char *) dentry->d_name.name;
-
-       err = p9_client_mknod_dotl(dfid, name, mode, rdev, gid, &qid);
-       if (err < 0)
-               goto error;
-
-       /* instantiate inode and assign the unopened fid to the dentry */
-       if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
-               fid = p9_client_walk(dfid, 1, &name, 1);
-               if (IS_ERR(fid)) {
-                       err = PTR_ERR(fid);
-                       P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
-                               err);
-                       fid = NULL;
-                       goto error;
-               }
-
-               inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
-               if (IS_ERR(inode)) {
-                       err = PTR_ERR(inode);
-                       P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n",
-                               err);
-                       goto error;
-               }
-               dentry->d_op = &v9fs_cached_dentry_operations;
-               d_instantiate(dentry, inode);
-               err = v9fs_fid_add(dentry, fid);
-               if (err < 0)
-                       goto error;
-               fid = NULL;
-       } else {
-               /*
-                * Not in cached mode. No need to populate inode with stat.
-                * socket syscall returns a fd, so we need instantiate
-                */
-               inode = v9fs_get_inode(dir->i_sb, mode);
-               if (IS_ERR(inode)) {
-                       err = PTR_ERR(inode);
-                       goto error;
-               }
-               dentry->d_op = &v9fs_dentry_operations;
-               d_instantiate(dentry, inode);
-       }
-       /* Now set the ACL based on the default value */
-       v9fs_set_create_acl(dentry, dacl, pacl);
-error:
-       if (fid)
-               p9_client_clunk(fid);
-       return err;
-}
 
-static int
-v9fs_vfs_readlink_dotl(struct dentry *dentry, char *buffer, int buflen)
-{
-       int retval;
-       struct p9_fid *fid;
-       char *target = NULL;
-
-       P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
-       retval = -EPERM;
-       fid = v9fs_fid_lookup(dentry);
-       if (IS_ERR(fid))
-               return PTR_ERR(fid);
-
-       retval = p9_client_readlink(fid, &target);
-       if (retval < 0)
-               return retval;
-
-       strncpy(buffer, target, buflen);
-       P9_DPRINTK(P9_DEBUG_VFS, "%s -> %s\n", dentry->d_name.name, buffer);
-
-       retval = strnlen(buffer, buflen);
-       return retval;
-}
-
-/**
- * v9fs_vfs_follow_link_dotl - follow a symlink path
- * @dentry: dentry for symlink
- * @nd: nameidata
- *
- */
-
-static void *
-v9fs_vfs_follow_link_dotl(struct dentry *dentry, struct nameidata *nd)
-{
-       int len = 0;
-       char *link = __getname();
-
-       P9_DPRINTK(P9_DEBUG_VFS, "%s n", dentry->d_name.name);
-
-       if (!link)
-               link = ERR_PTR(-ENOMEM);
-       else {
-               len = v9fs_vfs_readlink_dotl(dentry, link, PATH_MAX);
-               if (len < 0) {
-                       __putname(link);
-                       link = ERR_PTR(len);
-               } else
-                       link[min(len, PATH_MAX-1)] = 0;
-       }
-       nd_set_link(nd, link);
-
-       return NULL;
+       v9ses = v9fs_inode2v9ses(inode);
+       st = p9_client_stat(fid);
+       if (IS_ERR(st))
+               return PTR_ERR(st);
+       /*
+        * Don't update inode if the file type is different
+        */
+       umode = p9mode2unixmode(v9ses, st, &rdev);
+       if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
+               goto out;
+
+       spin_lock(&inode->i_lock);
+       /*
+        * We don't want to refresh inode->i_size,
+        * because we may have cached data
+        */
+       i_size = inode->i_size;
+       v9fs_stat2inode(st, inode, inode->i_sb);
+       if (v9ses->cache)
+               inode->i_size = i_size;
+       spin_unlock(&inode->i_lock);
+out:
+       p9stat_free(st);
+       kfree(st);
+       return 0;
 }
 
 static const struct inode_operations v9fs_dir_inode_operations_dotu = {
        .create = v9fs_vfs_create,
        .lookup = v9fs_vfs_lookup,
+       .atomic_open = v9fs_vfs_atomic_open,
        .symlink = v9fs_vfs_symlink,
        .link = v9fs_vfs_link,
        .unlink = v9fs_vfs_unlink,
@@ -2073,28 +1511,10 @@ static const struct inode_operations v9fs_dir_inode_operations_dotu = {
        .setattr = v9fs_vfs_setattr,
 };
 
-static const struct inode_operations v9fs_dir_inode_operations_dotl = {
-       .create = v9fs_vfs_create_dotl,
-       .lookup = v9fs_vfs_lookup,
-       .link = v9fs_vfs_link_dotl,
-       .symlink = v9fs_vfs_symlink_dotl,
-       .unlink = v9fs_vfs_unlink,
-       .mkdir = v9fs_vfs_mkdir_dotl,
-       .rmdir = v9fs_vfs_rmdir,
-       .mknod = v9fs_vfs_mknod_dotl,
-       .rename = v9fs_vfs_rename,
-       .getattr = v9fs_vfs_getattr_dotl,
-       .setattr = v9fs_vfs_setattr_dotl,
-       .setxattr = generic_setxattr,
-       .getxattr = generic_getxattr,
-       .removexattr = generic_removexattr,
-       .listxattr = v9fs_listxattr,
-       .check_acl = v9fs_check_acl,
-};
-
 static const struct inode_operations v9fs_dir_inode_operations = {
        .create = v9fs_vfs_create,
        .lookup = v9fs_vfs_lookup,
+       .atomic_open = v9fs_vfs_atomic_open,
        .unlink = v9fs_vfs_unlink,
        .mkdir = v9fs_vfs_mkdir,
        .rmdir = v9fs_vfs_rmdir,
@@ -2109,16 +1529,6 @@ static const struct inode_operations v9fs_file_inode_operations = {
        .setattr = v9fs_vfs_setattr,
 };
 
-static const struct inode_operations v9fs_file_inode_operations_dotl = {
-       .getattr = v9fs_vfs_getattr_dotl,
-       .setattr = v9fs_vfs_setattr_dotl,
-       .setxattr = generic_setxattr,
-       .getxattr = generic_getxattr,
-       .removexattr = generic_removexattr,
-       .listxattr = v9fs_listxattr,
-       .check_acl = v9fs_check_acl,
-};
-
 static const struct inode_operations v9fs_symlink_inode_operations = {
        .readlink = generic_readlink,
        .follow_link = v9fs_vfs_follow_link,
@@ -2127,14 +1537,3 @@ static const struct inode_operations v9fs_symlink_inode_operations = {
        .setattr = v9fs_vfs_setattr,
 };
 
-static const struct inode_operations v9fs_symlink_inode_operations_dotl = {
-       .readlink = v9fs_vfs_readlink_dotl,
-       .follow_link = v9fs_vfs_follow_link_dotl,
-       .put_link = v9fs_vfs_put_link,
-       .getattr = v9fs_vfs_getattr_dotl,
-       .setattr = v9fs_vfs_setattr_dotl,
-       .setxattr = generic_setxattr,
-       .getxattr = generic_getxattr,
-       .removexattr = generic_removexattr,
-       .listxattr = v9fs_listxattr,
-};