]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - fs/9p/vfs_inode.c
Merge branches 'irq-fixes-for-linus' and 'sched-fixes-for-linus' of git://git.kernel...
[mirror_ubuntu-bionic-kernel.git] / fs / 9p / vfs_inode.c
index b76a40bdf4c20e736fa908fa4d575cde91952d32..8a2c232f708a022d44d72522544697d84233aa08 100644 (file)
@@ -203,26 +203,25 @@ v9fs_blank_wstat(struct p9_wstat *wstat)
        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;
+       v9inode->fscache_key = NULL;
+       spin_lock_init(&v9inode->fscache_lock);
+#endif
+       v9inode->writeback_fid = NULL;
+       v9inode->cache_validity = 0;
+       return &v9inode->vfs_inode;
 }
 
 /**
@@ -234,35 +233,18 @@ static void v9fs_i_callback(struct rcu_head *head)
 {
        struct inode *inode = container_of(head, struct inode, i_rcu);
        INIT_LIST_HEAD(&inode->i_dentry);
-       kmem_cache_free(vcookie_cache, v9fs_inode2cookie(inode));
+       kmem_cache_free(v9fs_inode_cache, V9FS_I(inode));
 }
 
 void v9fs_destroy_inode(struct inode *inode)
 {
        call_rcu(&inode->i_rcu, v9fs_i_callback);
 }
-#endif
 
-/**
- * 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, int mode)
 {
-       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;
@@ -292,14 +274,20 @@ 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 "
@@ -335,12 +323,37 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)
                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, int mode)
+{
+       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);
+       }
+       err = v9fs_init_inode(v9ses, inode, mode);
+       if (err) {
+               iput(inode);
+               return ERR_PTR(err);
+       }
+       return inode;
 }
 
 /*
@@ -403,6 +416,8 @@ 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);
        filemap_fdatawrite(inode->i_mapping);
@@ -410,41 +425,67 @@ void v9fs_evict_inode(struct inode *inode)
 #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;
+       }
 }
 
-struct inode *
-v9fs_inode(struct v9fs_session_info *v9ses, struct p9_fid *fid,
-       struct super_block *sb)
+static struct inode *v9fs_qid_iget(struct super_block *sb,
+                                  struct p9_qid *qid,
+                                  struct p9_wstat *st)
 {
-       int err, umode;
-       struct inode *ret = NULL;
-       struct p9_wstat *st;
-
-       st = p9_client_stat(fid);
-       if (IS_ERR(st))
-               return ERR_CAST(st);
+       int retval, umode;
+       unsigned long i_ino;
+       struct inode *inode;
+       struct v9fs_session_info *v9ses = sb->s_fs_info;
 
+       i_ino = v9fs_qid2ino(qid);
+       inode = iget_locked(sb, i_ino);
+       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.
+        */
        umode = p9mode2unixmode(v9ses, st->mode);
-       ret = v9fs_get_inode(sb, umode);
-       if (IS_ERR(ret)) {
-               err = PTR_ERR(ret);
+       retval = v9fs_init_inode(v9ses, inode, umode);
+       if (retval)
                goto error;
-       }
-
-       v9fs_stat2inode(st, ret, sb);
-       ret->i_ino = v9fs_qid2ino(&st->qid);
 
+       v9fs_stat2inode(st, inode, sb);
 #ifdef CONFIG_9P_FSCACHE
-       v9fs_vcookie_set_qid(ret, &st->qid);
-       v9fs_cache_inode_get_cookie(ret);
+       v9fs_fscache_set_key(inode, &st->qid);
+       v9fs_cache_inode_get_cookie(inode);
 #endif
-       p9stat_free(st);
-       kfree(st);
-       return ret;
+       unlock_new_inode(inode);
+       return inode;
 error:
+       unlock_new_inode(inode);
+       iput(inode);
+       return ERR_PTR(retval);
+
+}
+
+struct inode *
+v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
+                   struct super_block *sb)
+{
+       struct p9_wstat *st;
+       struct inode *inode = NULL;
+
+       st = p9_client_stat(fid);
+       if (IS_ERR(st))
+               return ERR_CAST(st);
+
+       inode = v9fs_qid_iget(sb, &st->qid, st);
        p9stat_free(st);
        kfree(st);
-       return ERR_PTR(err);
+       return inode;
 }
 
 /**
@@ -458,8 +499,8 @@ error:
 static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
 {
        int retval;
-       struct inode *file_inode;
        struct p9_fid *v9fid;
+       struct inode *file_inode;
 
        P9_DPRINTK(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file,
                rmdir);
@@ -470,8 +511,20 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
                return PTR_ERR(v9fid);
 
        retval = p9_client_remove(v9fid);
-       if (!retval)
-               drop_nlink(file_inode);
+       if (!retval) {
+               /*
+                * directories on unlink should have zero
+                * link count
+                */
+               if (rmdir) {
+                       clear_nlink(file_inode);
+                       drop_nlink(dir);
+               } else
+                       drop_nlink(file_inode);
+
+               v9fs_invalidate_inode_attr(file_inode);
+               v9fs_invalidate_inode_attr(dir);
+       }
        return retval;
 }
 
@@ -531,7 +584,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
        }
 
        /* instantiate inode and assign the unopened fid to the dentry */
-       inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
+       inode = v9fs_get_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);
@@ -570,9 +623,10 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
        int err;
        u32 perm;
        int flags;
-       struct v9fs_session_info *v9ses;
-       struct p9_fid *fid;
        struct file *filp;
+       struct v9fs_inode *v9inode;
+       struct v9fs_session_info *v9ses;
+       struct p9_fid *fid, *inode_fid;
 
        err = 0;
        fid = NULL;
@@ -592,8 +646,25 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
                goto error;
        }
 
+       v9fs_invalidate_inode_attr(dir);
        /* if we are opening a file, assign the open fid to the file */
        if (nd && nd->flags & LOOKUP_OPEN) {
+               v9inode = V9FS_I(dentry->d_inode);
+               if (v9ses->cache && !v9inode->writeback_fid) {
+                       /*
+                        * 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);
+                               goto error;
+                       }
+                       v9inode->writeback_fid = (void *) inode_fid;
+               }
                filp = lookup_instantiate_filp(nd, dentry, generic_file_open);
                if (IS_ERR(filp)) {
                        err = PTR_ERR(filp);
@@ -601,6 +672,10 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
                }
 
                filp->private_data = fid;
+#ifdef CONFIG_9P_FSCACHE
+               if (v9ses->cache)
+                       v9fs_cache_inode_set_cookie(dentry->d_inode, filp);
+#endif
        } else
                p9_client_clunk(fid);
 
@@ -625,8 +700,8 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int 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);
        err = 0;
@@ -636,6 +711,9 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (IS_ERR(fid)) {
                err = PTR_ERR(fid);
                fid = NULL;
+       } else {
+               inc_nlink(dir);
+               v9fs_invalidate_inode_attr(dir);
        }
 
        if (fid)
@@ -687,7 +765,7 @@ struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
                return ERR_PTR(result);
        }
 
-       inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
+       inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
        if (IS_ERR(inode)) {
                result = PTR_ERR(inode);
                inode = NULL;
@@ -747,17 +825,19 @@ 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");
        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))
@@ -798,9 +878,30 @@ 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);
+                       /*
+                        * Work around vfs rename rehash bug with
+                        * FS_RENAME_DOES_D_MOVE
+                        */
+                       v9fs_invalidate_inode_attr(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);
 
@@ -831,9 +932,10 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
        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);
-
+       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);
@@ -891,17 +993,20 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
                if (iattr->ia_valid & ATTR_GID)
                        wstat.n_gid = iattr->ia_gid;
        }
-
-       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;
        }
+       /* 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;
+       v9fs_invalidate_inode_attr(dentry->d_inode);
 
        setattr_copy(dentry->d_inode, iattr);
        mark_inode_dirty(dentry->d_inode);
@@ -924,6 +1029,7 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
        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;
 
@@ -983,6 +1089,7 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
 
        /* not real number of blocks, but 512 byte ones ... */
        inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9;
+       v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;
 }
 
 /**
@@ -1115,8 +1222,8 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
        int mode, 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)) {
@@ -1130,6 +1237,7 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
        if (IS_ERR(fid))
                return PTR_ERR(fid);
 
+       v9fs_invalidate_inode_attr(dir);
        p9_client_clunk(fid);
        return 0;
 }
@@ -1166,8 +1274,8 @@ 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,
@@ -1186,7 +1294,10 @@ 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;
@@ -1237,6 +1348,32 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
        return retval;
 }
 
+int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
+{
+       loff_t i_size;
+       struct p9_wstat *st;
+       struct v9fs_session_info *v9ses;
+
+       v9ses = v9fs_inode2v9ses(inode);
+       st = p9_client_stat(fid);
+       if (IS_ERR(st))
+               return PTR_ERR(st);
+
+       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);
+       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,