GIT fe09f301169477ec059ea3e762ea1328f45ad7e8 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs.git#v9fs-devel commit fe09f301169477ec059ea3e762ea1328f45ad7e8 Author: Eric Van Hensbergen Date: Fri Feb 16 09:32:01 2007 -0600 9p: add write-cache support to loose cache mode (take 3) Loose cache mode was added primarily to asssist exclusive, read-only mounts (like venti) -- however, there is also a case for using loose write cacheing in support of read/write exclusive mounts. This feature is linked to the loose cache option and is disabled by default. This code adds the necessary code to support writes through the page cache. Write caches are not used for synthetic files or for files opened in APPEND mode. Signed-of-by: Eric Van Hensbergen commit a73e52e45bce435257d01c744a79bd436a1d65a7 Author: Eric Van Hensbergen Date: Sun Feb 11 13:21:39 2007 -0600 9p: implement optional loose read cache While cacheing is generally frowned upon in the 9p world, it has its place -- particularly in situations where the remote file system is exclusive and/or read-only. The vacfs views of venti content addressable store are a real-world instance of such a situation. To facilitate higher performance for these workloads (and eventually use the fscache patches), we have enabled a "loose" cache mode which does not attempt to maintain any form of consistency on the page-cache or dcache. This results in over two orders of magnitude performance improvement for cacheable block reads in the Bonnie benchmark. The more aggressive use of the dcache also seems to improve metadata operational performance. Signed-off-by: Eric Van Hensbergen commit aa688bda9f7a3717856e0c9ac11fc5b524f541aa Author: Eric W. Biederman Date: Tue Dec 12 15:26:07 2006 -0700 9p: Use kthread_stop instead of sending a SIGKILL. Since the kthread api does not bump the reference count on processes that tracked it is not safe allow user space to kill the threads, as I still retain a pointer to the task_struct. Signed-off-by: Eric W. Biederman Acked-by: Eric Van Hensbergen Signed-off-by: Andrew Morton --- Documentation/filesystems/00-INDEX | 2 Documentation/filesystems/9p.txt | 4 fs/9p/9p.h | 2 fs/9p/conv.c | 18 ++ fs/9p/conv.h | 2 fs/9p/fcall.c | 10 - fs/9p/fid.c | 19 +- fs/9p/fid.h | 2 fs/9p/mux.c | 5 fs/9p/v9fs.c | 9 + fs/9p/v9fs.h | 9 + fs/9p/v9fs_vfs.h | 4 fs/9p/vfs_addr.c | 181 +++++++++++++++++++++++++-- fs/9p/vfs_dentry.c | 26 +++ fs/9p/vfs_dir.c | 2 fs/9p/vfs_file.c | 71 +++++++++- fs/9p/vfs_inode.c | 40 ++++- fs/9p/vfs_super.c | 3 18 files changed, 358 insertions(+), 51 deletions(-) diff -puN Documentation/filesystems/00-INDEX~git-v9fs Documentation/filesystems/00-INDEX --- a/Documentation/filesystems/00-INDEX~git-v9fs +++ a/Documentation/filesystems/00-INDEX @@ -4,6 +4,8 @@ Exporting - explanation of how to make filesystems exportable. Locking - info on locking rules as they pertain to Linux VFS. +9p.txt + - 9p (v9fs) is an implementation of the Plan 9 remote fs protocol. adfs.txt - info and mount options for the Acorn Advanced Disc Filing System. afs.txt diff -puN Documentation/filesystems/9p.txt~git-v9fs Documentation/filesystems/9p.txt --- a/Documentation/filesystems/9p.txt~git-v9fs +++ a/Documentation/filesystems/9p.txt @@ -40,6 +40,10 @@ OPTIONS aname=name aname specifies the file tree to access when the server is offering several exported file systems. + cache=mode specifies a cacheing policy. By default, no caches are used. + loose = no attempts are made at consistency, + intended for exclusive, read-only mounts + debug=n specifies debug level. The debug level is a bitmask. 0x01 = display verbose error messages 0x02 = developer debug (DEBUG_CURRENT) diff -puN fs/9p/9p.h~git-v9fs fs/9p/9p.h --- a/fs/9p/9p.h~git-v9fs +++ a/fs/9p/9p.h @@ -370,6 +370,6 @@ int v9fs_t_read(struct v9fs_session_info u64 offset, u32 count, struct v9fs_fcall **rcall); int v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, - u32 count, const char __user * data, + u32 count, const char __user * data, char * kdata, struct v9fs_fcall **rcall); int v9fs_printfcall(char *, int, struct v9fs_fcall *, int); diff -puN fs/9p/conv.c~git-v9fs fs/9p/conv.c --- a/fs/9p/conv.c~git-v9fs +++ a/fs/9p/conv.c @@ -458,6 +458,15 @@ v9fs_put_user_data(struct cbuf *bufp, co return copy_from_user(*pdata, data, count); } +static int +v9fs_put_kernel_data(struct cbuf *bufp, char * kdata, int count, + unsigned char **pdata) +{ + *pdata = buf_alloc(bufp, count); + memcpy(*pdata, kdata, count); + return 0; +} + static void v9fs_put_wstat(struct cbuf *bufp, struct v9fs_wstat *wstat, struct v9fs_stat *stat, int statsz, int extended) @@ -723,7 +732,7 @@ struct v9fs_fcall *v9fs_create_tread(u32 } struct v9fs_fcall *v9fs_create_twrite(u32 fid, u64 offset, u32 count, - const char __user * data) + const char __user * data, char * kdata) { int size, err; struct v9fs_fcall *fc; @@ -738,7 +747,12 @@ struct v9fs_fcall *v9fs_create_twrite(u3 v9fs_put_int32(bufp, fid, &fc->params.twrite.fid); v9fs_put_int64(bufp, offset, &fc->params.twrite.offset); v9fs_put_int32(bufp, count, &fc->params.twrite.count); - err = v9fs_put_user_data(bufp, data, count, &fc->params.twrite.data); + if(data) + err = v9fs_put_user_data(bufp, data, count, + &fc->params.twrite.data); + else + err = v9fs_put_kernel_data(bufp, kdata, count, + &fc->params.twrite.data); if (err) { kfree(fc); fc = ERR_PTR(err); diff -puN fs/9p/conv.h~git-v9fs fs/9p/conv.h --- a/fs/9p/conv.h~git-v9fs +++ a/fs/9p/conv.h @@ -42,7 +42,7 @@ struct v9fs_fcall *v9fs_create_tcreate(u char *extension, int extended); struct v9fs_fcall *v9fs_create_tread(u32 fid, u64 offset, u32 count); struct v9fs_fcall *v9fs_create_twrite(u32 fid, u64 offset, u32 count, - const char __user *data); + const char __user *data, char *kdata); struct v9fs_fcall *v9fs_create_tclunk(u32 fid); struct v9fs_fcall *v9fs_create_tremove(u32 fid); struct v9fs_fcall *v9fs_create_tstat(u32 fid); diff -puN fs/9p/fcall.c~git-v9fs fs/9p/fcall.c --- a/fs/9p/fcall.c~git-v9fs +++ a/fs/9p/fcall.c @@ -367,7 +367,7 @@ v9fs_t_read(struct v9fs_session_info *v9 int ret; struct v9fs_fcall *tc, *rc; - dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid, + dprintk(DEBUG_9P, "fid %d offset 0x%llx count 0x%x\n", fid, (long long unsigned) offset, count); tc = v9fs_create_tread(fid, offset, count); @@ -393,21 +393,23 @@ v9fs_t_read(struct v9fs_session_info *v9 * @fid: fid to write to * @offset: offset to start write at * @count: how many bytes to write + * @data: userspace data + * @kdata: kernelspace data * @fcall: pointer to response fcall * */ int v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, u32 count, - const char __user *data, struct v9fs_fcall **rcp) + const char __user *data, char *kdata, struct v9fs_fcall **rcp) { int ret; struct v9fs_fcall *tc, *rc; - dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid, + dprintk(DEBUG_9P, "fid %d offset 0x%llx count 0x%x\n", fid, (long long unsigned) offset, count); - tc = v9fs_create_twrite(fid, offset, count, data); + tc = v9fs_create_twrite(fid, offset, count, data, kdata); if (!IS_ERR(tc)) { ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); diff -puN fs/9p/fid.c~git-v9fs fs/9p/fid.c --- a/fs/9p/fid.c~git-v9fs +++ a/fs/9p/fid.c @@ -40,19 +40,18 @@ * */ -int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry) +int v9fs_fid_insert(struct v9fs_fid *fid, void **priv) { - struct list_head *fid_list = (struct list_head *)dentry->d_fsdata; - dprintk(DEBUG_9P, "fid %d (%p) dentry %s (%p)\n", fid->fid, fid, - dentry->d_iname, dentry); - if (dentry->d_fsdata == NULL) { - dentry->d_fsdata = + struct list_head *fid_list= (struct list_head *) *priv; + + if (fid_list == NULL) { + *priv = kmalloc(sizeof(struct list_head), GFP_KERNEL); - if (dentry->d_fsdata == NULL) { + if (*priv == NULL) { dprintk(DEBUG_ERROR, "Out of memory\n"); return -ENOMEM; } - fid_list = (struct list_head *)dentry->d_fsdata; + fid_list = (struct list_head *)*priv; INIT_LIST_HEAD(fid_list); /* Initialize list head */ } @@ -80,6 +79,7 @@ struct v9fs_fid *v9fs_fid_create(struct new->fid = fid; new->v9ses = v9ses; + new->filp = NULL; new->fidopen = 0; new->fidclunked = 0; new->iounit = 0; @@ -136,7 +136,8 @@ struct v9fs_fid *v9fs_fid_lookup(struct } /** - * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and release it + * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and + * release it * @dentry: dentry to look for fid in * * find a fid in the dentry and then clone to a new private fid diff -puN fs/9p/fid.h~git-v9fs fs/9p/fid.h --- a/fs/9p/fid.h~git-v9fs +++ a/fs/9p/fid.h @@ -56,7 +56,7 @@ struct v9fs_fid *v9fs_fid_lookup(struct struct v9fs_fid *v9fs_fid_get_created(struct dentry *); void v9fs_fid_destroy(struct v9fs_fid *fid); struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *, int fid); -int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry); +int v9fs_fid_insert(struct v9fs_fid *fid, void **priv); struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry); void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid); diff -puN fs/9p/mux.c~git-v9fs fs/9p/mux.c --- a/fs/9p/mux.c~git-v9fs +++ a/fs/9p/mux.c @@ -256,7 +256,7 @@ static void v9fs_mux_poll_stop(struct v9 vpt->muxnum--; if (!vpt->muxnum) { dprintk(DEBUG_MUX, "destroy proc %p\n", vpt); - send_sig(SIGKILL, vpt->task, 1); + kthread_stop(vpt->task); vpt->task = NULL; v9fs_mux_poll_task_num--; } @@ -438,11 +438,8 @@ static int v9fs_poll_proc(void *a) vpt = a; dprintk(DEBUG_MUX, "start %p %p\n", current, vpt); - allow_signal(SIGKILL); while (!kthread_should_stop()) { set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) - break; list_for_each_entry_safe(m, mtmp, &vpt->mux_list, mux_list) { v9fs_poll_mux(m); diff -puN fs/9p/v9fs.c~git-v9fs fs/9p/v9fs.c --- a/fs/9p/v9fs.c~git-v9fs +++ a/fs/9p/v9fs.c @@ -53,6 +53,8 @@ enum { Opt_uname, Opt_remotename, /* Options that take no arguments */ Opt_legacy, Opt_nodevmap, Opt_unix, Opt_tcp, Opt_fd, + /* Cache options */ + Opt_cache_loose, /* Error token */ Opt_err }; @@ -76,6 +78,8 @@ static match_table_t tokens = { {Opt_fd, "fd"}, {Opt_legacy, "noextend"}, {Opt_nodevmap, "nodevmap"}, + {Opt_cache_loose, "cache=loose"}, + {Opt_cache_loose, "loose"}, {Opt_err, NULL} }; @@ -106,6 +110,7 @@ static void v9fs_parse_options(char *opt v9ses->debug = 0; v9ses->rfdno = ~0; v9ses->wfdno = ~0; + v9ses->cache = 0; if (!options) return; @@ -121,7 +126,6 @@ static void v9fs_parse_options(char *opt "integer field, but no integer?\n"); continue; } - } switch (token) { case Opt_port: @@ -169,6 +173,9 @@ static void v9fs_parse_options(char *opt case Opt_nodevmap: v9ses->nodev = 1; break; + case Opt_cache_loose: + v9ses->cache = CACHE_LOOSE; + break; default: continue; } diff -puN fs/9p/v9fs.h~git-v9fs fs/9p/v9fs.h --- a/fs/9p/v9fs.h~git-v9fs +++ a/fs/9p/v9fs.h @@ -47,7 +47,7 @@ struct v9fs_session_info { unsigned int afid; /* authentication fid */ unsigned int rfdno; /* read file descriptor number */ unsigned int wfdno; /* write file descriptor number */ - + unsigned int cache; /* cache mode */ char *name; /* user name to mount as */ char *remotename; /* name of remote hierarchy being mounted */ @@ -73,6 +73,13 @@ enum { PROTO_FD, }; +/* possible values of ->cache */ +/* eventually support loose, tight, time, session, default always none */ +enum { + CACHE_NONE, /* default */ + CACHE_LOOSE, /* no consistency */ +}; + extern struct dentry *v9fs_debugfs_root; int v9fs_session_init(struct v9fs_session_info *, const char *, char *); diff -puN fs/9p/v9fs_vfs.h~git-v9fs fs/9p/v9fs_vfs.h --- a/fs/9p/v9fs_vfs.h~git-v9fs +++ a/fs/9p/v9fs_vfs.h @@ -40,8 +40,11 @@ extern struct file_system_type v9fs_fs_type; extern const struct address_space_operations v9fs_addr_operations; extern const struct file_operations v9fs_file_operations; +extern const struct file_operations v9fs_cached_file_operations; extern const struct file_operations v9fs_dir_operations; extern struct dentry_operations v9fs_dentry_operations; +extern struct dentry_operations v9fs_cached_dentry_operations; +extern const struct inode_operations v9fs_cached_file_inode_operations; struct inode *v9fs_get_inode(struct super_block *sb, int mode); ino_t v9fs_qid2ino(struct v9fs_qid *qid); @@ -51,3 +54,4 @@ int v9fs_file_open(struct inode *inode, void v9fs_inode2stat(struct inode *inode, struct v9fs_stat *stat); void v9fs_dentry_release(struct dentry *); int v9fs_uflags2omode(int uflags); +ssize_t v9fs_write(struct file *filp, const char __user * data, char * kdata, size_t count, loff_t * offset); diff -puN fs/9p/vfs_addr.c~git-v9fs fs/9p/vfs_addr.c --- a/fs/9p/vfs_addr.c~git-v9fs +++ a/fs/9p/vfs_addr.c @@ -6,6 +6,9 @@ * Copyright (C) 2005 by Eric Van Hensbergen * Copyright (C) 2002 by Ron Minnich * + * Some operations based on code from fs/cifs/file.c + * Copyright (C) International Business Machines Corp., 2002,2003 + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. @@ -41,14 +44,14 @@ #include "fid.h" /** - * v9fs_vfs_readpage - read an entire page in from 9P + * v9fs_vfs_readpage_worker - read an entire page in from 9P * * @file: file being read * @page: structure to page * */ -static int v9fs_vfs_readpage(struct file *filp, struct page *page) +static int v9fs_vfs_readpage_worker(struct file *filp, struct page *page) { char *buffer = NULL; int retval = -EIO; @@ -63,19 +66,18 @@ static int v9fs_vfs_readpage(struct file int total = 0; int result = 0; + page_cache_get(page); buffer = kmap(page); do { if (count < rsize) rsize = count; result = v9fs_t_read(v9ses, fid, offset, rsize, &fcall); - if (result < 0) { - printk(KERN_ERR "v9fs_t_read returned %d\n", - result); + printk(KERN_ERR "v9fs_t_read returned %d\n", result); kfree(fcall); - goto UnmapAndUnlock; + goto io_error; } else offset += result; @@ -96,12 +98,175 @@ static int v9fs_vfs_readpage(struct file SetPageUptodate(page); retval = 0; -UnmapAndUnlock: + io_error: kunmap(page); + page_cache_release(page); + return retval; +} + +/** + * v9fs_prepare_write - prepare for mmap or page-cache I/O + * + * @file: file being read + * @page: structure to page + * @from: starting offset + * @to: ending offset + * + */ + +int v9fs_prepare_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + if(!PageUptodate(page)) { + if (to - from != PAGE_CACHE_SIZE) { + void *kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr, 0, from); + memset(kaddr + to, 0, PAGE_CACHE_SIZE - to); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + } + if ((file->f_flags & O_ACCMODE) != O_WRONLY) + v9fs_vfs_readpage_worker(file, page); + } + + return 0; +} + +/** + * v9fs_commit_write - prepare for mmap or page-cache I/O + * + * @file: file being read + * @page: structure to page + * @offset: starting offset + * @to: ending offset + * + * TODO: Perhaps I should just do the write here? + */ + +int v9fs_commit_write(struct file *file, struct page *page, + unsigned offset, unsigned to) +{ + struct inode *inode = page->mapping->host; + loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; + + dprintk(DEBUG_VFS, "\n"); + + if(!PageUptodate(page)) + SetPageUptodate(page); + + /* + * No need to use i_size_read() here, the i_size + * cannot change under us because we hold the i_mutex. + * + */ + if (pos > inode->i_size) + i_size_write(inode, pos); + set_page_dirty(page); + dprintk(DEBUG_CURRENT, "inode->size: %llx\n",inode->i_size); + + return 0; +} + + + +/** + * v9fs_vfs_readpage - read an entire page in from 9P + * + * @file: file being read + * @page: structure to page + * + */ + +static int v9fs_vfs_readpage(struct file *filp, struct page *page) +{ + int retval; + + dprintk(DEBUG_VFS, "\n"); + + retval = v9fs_vfs_readpage_worker(filp, page); + unlock_page(page); return retval; } +/** + * v9fs_find_file - find a file pointer based on page + * @page: page to lookup file based on + * + */ +static struct file *v9fs_find_file(struct page *page) +{ + struct inode *inode = page->mapping->host; + struct v9fs_fid *fid = NULL; + struct list_head *p; + + dprintk(DEBUG_VFS, " page: %p\n", page); + + p = inode->i_private; + if (!p) { + dprintk(DEBUG_ERROR, "No list_head\n"); + return NULL; + } + + fid = list_entry(p->next, struct v9fs_fid, list); + + if (fid) + return fid->filp; + + dprintk(DEBUG_VFS, " filp not found for page: %p\n", page); + return NULL; +} + +/** + * v9fs_vfs_writepage - write a mmaped page to server + * @page: page to write + * @wbc: writeback control + * + */ + +static int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc) +{ + char *buffer = NULL; + struct address_space *mapping = page->mapping; + int retval = -EIO; + loff_t offset = 0; + pgoff_t end_index; + int count = PAGE_CACHE_SIZE; + struct file *filp = v9fs_find_file(page); + struct inode *inode = mapping->host; + + dprintk(DEBUG_VFS, "page: %p\n", page); + + if ((!inode) || (!filp)) + goto UnlockPage; + + end_index = inode->i_size >> PAGE_CACHE_SHIFT; + + /* complicated case at end of file */ + if (page->index >= end_index) { + /* things got complicated... */ + count = inode->i_size & (PAGE_CACHE_SIZE - 1); + if (page->index >= end_index + 1 || !count) + return 0; /* truncated - don't care */ + } + + buffer = kmap(page); + offset = ((loff_t) page->index << PAGE_CACHE_SHIFT); + page_cache_get(page); + retval = v9fs_write(filp, NULL, buffer, count, &offset); + + kunmap(page); + + UnlockPage: + unlock_page(page); + page_cache_release(page); + + return retval; +} + const struct address_space_operations v9fs_addr_operations = { - .readpage = v9fs_vfs_readpage, + .prepare_write = v9fs_prepare_write, + .commit_write = v9fs_commit_write, + .readpage = v9fs_vfs_readpage, + .writepage = v9fs_vfs_writepage, }; diff -puN fs/9p/vfs_dentry.c~git-v9fs fs/9p/vfs_dentry.c --- a/fs/9p/vfs_dentry.c~git-v9fs +++ a/fs/9p/vfs_dentry.c @@ -53,10 +53,31 @@ static int v9fs_dentry_delete(struct dentry *dentry) { dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); + return 1; } /** + * v9fs_cached_dentry_delete - called when dentry refcount equals 0 + * @dentry: dentry in question + * + * Only return 1 if our inode is invalid. Only non-synthetic files + * (ones without mtime == 0) should be calling this function. + * + */ + +static int v9fs_cached_dentry_delete(struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); + + if(!inode) + return 1; + + return 0; +} + +/** * v9fs_dentry_release - called when dentry is going to be freed * @dentry: dentry that is being release * @@ -87,6 +108,11 @@ void v9fs_dentry_release(struct dentry * } } +struct dentry_operations v9fs_cached_dentry_operations = { + .d_delete = v9fs_cached_dentry_delete, + .d_release = v9fs_dentry_release, +}; + struct dentry_operations v9fs_dentry_operations = { .d_delete = v9fs_dentry_delete, .d_release = v9fs_dentry_release, diff -puN fs/9p/vfs_dir.c~git-v9fs fs/9p/vfs_dir.c --- a/fs/9p/vfs_dir.c~git-v9fs +++ a/fs/9p/vfs_dir.c @@ -197,7 +197,7 @@ int v9fs_dir_release(struct inode *inode dprintk(DEBUG_ERROR, "clunk failed\n"); kfree(fid->rdir_fcall); - kfree(fid); + v9fs_fid_destroy(fid); filp->private_data = NULL; } diff -puN fs/9p/vfs_file.c~git-v9fs fs/9p/vfs_file.c --- a/fs/9p/vfs_file.c~git-v9fs +++ a/fs/9p/vfs_file.c @@ -79,6 +79,16 @@ int v9fs_file_open(struct inode *inode, vfid->filp = file; kfree(fcall); + if((vfid->qid.version) && (v9ses->cache) && !(file->f_flags&O_APPEND)) { + /* enable cached file options */ + if(file->f_op == &v9fs_file_operations) { + file->f_op = &v9fs_cached_file_operations; + inode->i_op = &v9fs_cached_file_inode_operations; + } + } + + v9fs_fid_insert(vfid, &inode->i_private); + return 0; Clunk_Fid: @@ -180,17 +190,18 @@ v9fs_file_read(struct file *filp, char _ } /** - * v9fs_file_write - write to a file + * v9fs_write - write to a file (generic) * @filep: file pointer to write - * @data: data buffer to write data from + * @data: user data buffer to write data from + * @kdata: kernel data buffer to write data from * @count: size of buffer * @offset: offset at which to write data * */ -static ssize_t -v9fs_file_write(struct file *filp, const char __user * data, - size_t count, loff_t * offset) +ssize_t +v9fs_write(struct file *filp, const char __user * data, + char* kdata, size_t count, loff_t * offset) { struct inode *inode = filp->f_path.dentry->d_inode; struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); @@ -201,8 +212,8 @@ v9fs_file_write(struct file *filp, const int rsize = 0; int total = 0; - dprintk(DEBUG_VFS, "data %p count %d offset %x\n", data, (int)count, - (int)*offset); + dprintk(DEBUG_VFS, "count %d offset %x\n", + (int)count, (int)*offset); rsize = v9ses->maxdata - V9FS_IOHDRSZ; if (v9fid->iounit != 0 && rsize > v9fid->iounit) rsize = v9fid->iounit; @@ -211,7 +222,8 @@ v9fs_file_write(struct file *filp, const if (count < rsize) rsize = count; - result = v9fs_t_write(v9ses, fid, *offset, rsize, data, &fcall); + result = v9fs_t_write(v9ses, fid, *offset, rsize, + data, kdata, &fcall); if (result < 0) { PRINT_FCALL_ERROR("error while writing", fcall); kfree(fcall); @@ -230,14 +242,53 @@ v9fs_file_write(struct file *filp, const } count -= result; - data += result; + if(data) + data += result; + else + kdata += result; total += result; } while (count); - invalidate_inode_pages2(inode->i_mapping); return total; } + +/** + * v9fs_file_write - write to a file + * @filep: file pointer to write + * @data: data buffer to write data from + * @count: size of buffer + * @offset: offset at which to write data + * + */ + +static ssize_t +v9fs_file_write(struct file *filp, const char __user * data, + size_t count, loff_t * offset) +{ + struct inode *inode = filp->f_path.dentry->d_inode; + ssize_t ret; + + dprintk(DEBUG_VFS, "count %d offset %x\n", + (int)count, (int)*offset); + ret = v9fs_write(filp, data, NULL, count, offset); + if(invalidate_inode_pages2(inode->i_mapping) < 0) + eprintk(KERN_WARNING, "failed to flush page cache on write\n"); + return ret; +} + +const struct file_operations v9fs_cached_file_operations = { + .llseek = generic_file_llseek, + .read = do_sync_read, + .aio_read = generic_file_aio_read, + .write = do_sync_write, + .aio_write = generic_file_aio_write, + .open = v9fs_file_open, + .release = v9fs_dir_release, + .lock = v9fs_file_lock, + .mmap = generic_file_mmap, +}; + const struct file_operations v9fs_file_operations = { .llseek = generic_file_llseek, .read = v9fs_file_read, diff -puN fs/9p/vfs_inode.c~git-v9fs fs/9p/vfs_inode.c --- a/fs/9p/vfs_inode.c~git-v9fs +++ a/fs/9p/vfs_inode.c @@ -208,6 +208,7 @@ struct inode *v9fs_get_inode(struct supe inode->i_rdev = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_mapping->a_ops = &v9fs_addr_operations; + inode->i_private = NULL; switch (mode & S_IFMT) { case S_IFIFO: @@ -340,7 +341,7 @@ v9fs_clone_walk(struct v9fs_session_info goto clunk_fid; } - err = v9fs_fid_insert(ret, dentry); + err = v9fs_fid_insert(ret, &dentry->d_fsdata); if (err < 0) { v9fs_fid_destroy(ret); goto clunk_fid; @@ -504,7 +505,10 @@ v9fs_vfs_create(struct inode *dir, struc goto error; } - dentry->d_op = &v9fs_dentry_operations; + if(v9ses->cache) + dentry->d_op = &v9fs_cached_dentry_operations; + else + dentry->d_op = &v9fs_dentry_operations; d_instantiate(dentry, inode); if (nd && nd->flags & LOOKUP_OPEN) { @@ -517,13 +521,22 @@ v9fs_vfs_create(struct inode *dir, struc v9fs_fid_destroy(ffid); return PTR_ERR(filp); } - ffid->rdir_pos = 0; ffid->rdir_fcall = NULL; ffid->fidopen = 1; ffid->iounit = iounit; ffid->filp = filp; filp->private_data = ffid; + if((vfid->qid.version) && (v9ses->cache) && + !(flags & O_APPEND)) { + /* enable cached file options */ + if(filp->f_op == &v9fs_file_operations) { + filp->f_op = &v9fs_cached_file_operations; + inode->i_op = + &v9fs_cached_file_inode_operations; + } + } + v9fs_fid_insert(ffid, &inode->i_private); } return 0; @@ -589,7 +602,10 @@ static int v9fs_vfs_mkdir(struct inode * goto error; } - dentry->d_op = &v9fs_dentry_operations; + if(v9ses->cache) + dentry->d_op = &v9fs_cached_dentry_operations; + else + dentry->d_op = &v9fs_dentry_operations; d_instantiate(dentry, inode); return 0; @@ -626,7 +642,6 @@ static struct dentry *v9fs_vfs_lookup(st sb = dir->i_sb; v9ses = v9fs_inode2v9ses(dir); - dentry->d_op = &v9fs_dentry_operations; dirfid = v9fs_fid_lookup(dentry->d_parent); if(IS_ERR(dirfid)) @@ -691,12 +706,16 @@ static struct dentry *v9fs_vfs_lookup(st goto FreeFcall; } - result = v9fs_fid_insert(fid, dentry); + result = v9fs_fid_insert(fid, &dentry->d_fsdata); if (result < 0) goto FreeFcall; fid->qid = fcall->params.rstat.stat.qid; v9fs_stat2inode(&fcall->params.rstat.stat, inode, inode->i_sb); + if((fid->qid.version)&&(v9ses->cache)) + dentry->d_op = &v9fs_cached_dentry_operations; + else + dentry->d_op = &v9fs_dentry_operations; d_add(dentry, inode); kfree(fcall); @@ -1184,7 +1203,10 @@ static int v9fs_vfs_mkspecial(struct ino goto free_vfid; } - dentry->d_op = &v9fs_dentry_operations; + if(v9ses->cache) + dentry->d_op = &v9fs_cached_dentry_operations; + else + dentry->d_op = &v9fs_dentry_operations; d_instantiate(dentry, inode); return 0; @@ -1335,6 +1357,10 @@ static const struct inode_operations v9f .setattr = v9fs_vfs_setattr, }; +const struct inode_operations v9fs_cached_file_inode_operations = { + .setattr = v9fs_vfs_setattr, +}; + static const struct inode_operations v9fs_symlink_inode_operations = { .readlink = v9fs_vfs_readlink, .follow_link = v9fs_vfs_follow_link, diff -puN fs/9p/vfs_super.c~git-v9fs fs/9p/vfs_super.c --- a/fs/9p/vfs_super.c~git-v9fs +++ a/fs/9p/vfs_super.c @@ -56,6 +56,7 @@ static const struct super_operations v9f static void v9fs_clear_inode(struct inode *inode) { filemap_fdatawrite(inode->i_mapping); + kfree(inode->i_private); } /** @@ -167,7 +168,7 @@ static int v9fs_get_sb(struct file_syste goto put_back_sb; } - retval = v9fs_fid_insert(root_fid, root); + retval = v9fs_fid_insert(root_fid, &root->d_fsdata); if (retval < 0) { kfree(fcall); goto put_back_sb; _