diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2006-01-09 19:18:33 +0000 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-01-09 19:18:33 +0000 |
commit | 0a3a98f6dd4e8f4d928a09302c0d1c56f2192ac3 (patch) | |
tree | 92f55e374a84d06ce8213a4540454760fdecf137 /fs | |
parent | 8ef12c9f01afba47c2d33bb939085111ca0d0f7d (diff) | |
parent | 5367f2d67c7d0bf1faae90e6e7b4e2ac3c9b5e0f (diff) | |
download | op-kernel-dev-0a3a98f6dd4e8f4d928a09302c0d1c56f2192ac3.zip op-kernel-dev-0a3a98f6dd4e8f4d928a09302c0d1c56f2192ac3.tar.gz |
Merge Linus' tree.
Diffstat (limited to 'fs')
96 files changed, 2771 insertions, 1996 deletions
@@ -1,8 +1,9 @@ /* * linux/fs/9p/9p.c * - * This file contains functions 9P2000 functions + * This file contains functions to perform synchronous 9P calls * + * Copyright (C) 2004 by Latchesar Ionkov <lucho@ionkov.net> * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> * @@ -33,6 +34,7 @@ #include "debug.h" #include "v9fs.h" #include "9p.h" +#include "conv.h" #include "mux.h" /** @@ -46,16 +48,21 @@ int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, - char *version, struct v9fs_fcall **fcall) + char *version, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; dprintk(DEBUG_9P, "msize: %d version: %s\n", msize, version); - msg.id = TVERSION; - msg.params.tversion.msize = msize; - msg.params.tversion.version = version; + tc = v9fs_create_tversion(msize, version); - return v9fs_mux_rpc(v9ses, &msg, fcall); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -71,19 +78,45 @@ v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, int v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, - u32 fid, u32 afid, struct v9fs_fcall **fcall) + u32 fid, u32 afid, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall* tc; dprintk(DEBUG_9P, "uname '%s' aname '%s' fid %d afid %d\n", uname, aname, fid, afid); - msg.id = TATTACH; - msg.params.tattach.fid = fid; - msg.params.tattach.afid = afid; - msg.params.tattach.uname = uname; - msg.params.tattach.aname = aname; - return v9fs_mux_rpc(v9ses, &msg, fcall); + tc = v9fs_create_tattach(fid, afid, uname, aname); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; +} + +static void v9fs_t_clunk_cb(void *a, struct v9fs_fcall *tc, + struct v9fs_fcall *rc, int err) +{ + int fid; + struct v9fs_session_info *v9ses; + + if (err) + return; + + fid = tc->params.tclunk.fid; + kfree(tc); + + if (!rc) + return; + + dprintk(DEBUG_9P, "tcall id %d rcall id %d\n", tc->id, rc->id); + v9ses = a; + if (rc->id == RCLUNK) + v9fs_put_idpool(fid, &v9ses->fidpool); + + kfree(rc); } /** @@ -95,16 +128,25 @@ v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, */ int -v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_fcall **fcall) +v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc, *rc; dprintk(DEBUG_9P, "fid %d\n", fid); - msg.id = TCLUNK; - msg.params.tclunk.fid = fid; - return v9fs_mux_rpc(v9ses, &msg, fcall); + rc = NULL; + tc = v9fs_create_tclunk(fid); + if (!IS_ERR(tc)) + ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); + else + ret = PTR_ERR(tc); + + if (ret) + dprintk(DEBUG_ERROR, "failed fid %d err %d\n", fid, ret); + + v9fs_t_clunk_cb(v9ses, tc, rc, ret); + return ret; } /** @@ -114,14 +156,21 @@ v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid, * */ -int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 tag) +int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 oldtag) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; + + dprintk(DEBUG_9P, "oldtag %d\n", oldtag); + + tc = v9fs_create_tflush(oldtag); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, NULL); + kfree(tc); + } else + ret = PTR_ERR(tc); - dprintk(DEBUG_9P, "oldtag %d\n", tag); - msg.id = TFLUSH; - msg.params.tflush.oldtag = tag; - return v9fs_mux_rpc(v9ses, &msg, NULL); + return ret; } /** @@ -133,17 +182,22 @@ int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 tag) */ int -v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **fcall) +v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; dprintk(DEBUG_9P, "fid %d\n", fid); - if (fcall) - *fcall = NULL; - msg.id = TSTAT; - msg.params.tstat.fid = fid; - return v9fs_mux_rpc(v9ses, &msg, fcall); + ret = -ENOMEM; + tc = v9fs_create_tstat(fid); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -157,16 +211,21 @@ v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **fcall) int v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_stat *stat, struct v9fs_fcall **fcall) + struct v9fs_wstat *wstat, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; + + dprintk(DEBUG_9P, "fid %d\n", fid); - dprintk(DEBUG_9P, "fid %d length %d\n", fid, (int)stat->length); - msg.id = TWSTAT; - msg.params.twstat.fid = fid; - msg.params.twstat.stat = stat; + tc = v9fs_create_twstat(fid, wstat, v9ses->extended); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); - return v9fs_mux_rpc(v9ses, &msg, fcall); + return ret; } /** @@ -183,23 +242,27 @@ v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, int v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, - char *name, struct v9fs_fcall **fcall) + char *name, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; + int nwname; dprintk(DEBUG_9P, "fid %d newfid %d wname '%s'\n", fid, newfid, name); - msg.id = TWALK; - msg.params.twalk.fid = fid; - msg.params.twalk.newfid = newfid; - - if (name) { - msg.params.twalk.nwname = 1; - msg.params.twalk.wnames = &name; - } else { - msg.params.twalk.nwname = 0; - } - - return v9fs_mux_rpc(v9ses, &msg, fcall); + + if (name) + nwname = 1; + else + nwname = 0; + + tc = v9fs_create_twalk(fid, newfid, nwname, &name); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -214,19 +277,21 @@ v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, int v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode, - struct v9fs_fcall **fcall) + struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; - long errorno = -1; + int ret; + struct v9fs_fcall *tc; dprintk(DEBUG_9P, "fid %d mode %d\n", fid, mode); - msg.id = TOPEN; - msg.params.topen.fid = fid; - msg.params.topen.mode = mode; - errorno = v9fs_mux_rpc(v9ses, &msg, fcall); + tc = v9fs_create_topen(fid, mode); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); - return errorno; + return ret; } /** @@ -239,14 +304,21 @@ v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode, int v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_fcall **fcall) + struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; dprintk(DEBUG_9P, "fid %d\n", fid); - msg.id = TREMOVE; - msg.params.tremove.fid = fid; - return v9fs_mux_rpc(v9ses, &msg, fcall); + + tc = v9fs_create_tremove(fid); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -262,20 +334,22 @@ v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid, int v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name, - u32 perm, u8 mode, struct v9fs_fcall **fcall) + u32 perm, u8 mode, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; dprintk(DEBUG_9P, "fid %d name '%s' perm %x mode %d\n", fid, name, perm, mode); - msg.id = TCREATE; - msg.params.tcreate.fid = fid; - msg.params.tcreate.name = name; - msg.params.tcreate.perm = perm; - msg.params.tcreate.mode = mode; + tc = v9fs_create_tcreate(fid, name, perm, mode); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); - return v9fs_mux_rpc(v9ses, &msg, fcall); + return ret; } /** @@ -290,31 +364,29 @@ v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name, int v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset, - u32 count, struct v9fs_fcall **fcall) + u32 count, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; - struct v9fs_fcall *rc = NULL; - long errorno = -1; - - dprintk(DEBUG_9P, "fid %d offset 0x%lx count 0x%x\n", fid, - (long unsigned int)offset, count); - msg.id = TREAD; - msg.params.tread.fid = fid; - msg.params.tread.offset = offset; - msg.params.tread.count = count; - errorno = v9fs_mux_rpc(v9ses, &msg, &rc); - - if (!errorno) { - errorno = rc->params.rread.count; - dump_data(rc->params.rread.data, rc->params.rread.count); - } - - if (fcall) - *fcall = rc; - else - kfree(rc); - - return errorno; + int ret; + struct v9fs_fcall *tc, *rc; + + dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid, + (long long unsigned) offset, count); + + tc = v9fs_create_tread(fid, offset, count); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); + if (!ret) + ret = rc->params.rread.count; + if (rcp) + *rcp = rc; + else + kfree(rc); + + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -328,32 +400,30 @@ v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset, */ int -v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, - u64 offset, u32 count, void *data, struct v9fs_fcall **fcall) +v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, u32 count, + const char __user *data, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; - struct v9fs_fcall *rc = NULL; - long errorno = -1; + int ret; + struct v9fs_fcall *tc, *rc; - dprintk(DEBUG_9P, "fid %d offset 0x%llx count 0x%x\n", fid, - (unsigned long long)offset, count); - dump_data(data, count); + dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid, + (long long unsigned) offset, count); - msg.id = TWRITE; - msg.params.twrite.fid = fid; - msg.params.twrite.offset = offset; - msg.params.twrite.count = count; - msg.params.twrite.data = data; + tc = v9fs_create_twrite(fid, offset, count, data); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); - errorno = v9fs_mux_rpc(v9ses, &msg, &rc); + if (!ret) + ret = rc->params.rwrite.count; + if (rcp) + *rcp = rc; + else + kfree(rc); - if (!errorno) - errorno = rc->params.rwrite.count; + kfree(tc); + } else + ret = PTR_ERR(tc); - if (fcall) - *fcall = rc; - else - kfree(rc); - - return errorno; + return ret; } + @@ -3,6 +3,7 @@ * * 9P protocol definitions. * + * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> * @@ -100,9 +101,18 @@ enum { V9FS_QTFILE = 0x00, }; +#define V9FS_NOTAG (u16)(~0) +#define V9FS_NOFID (u32)(~0) +#define V9FS_MAXWELEM 16 + /* ample room for Twrite/Rread header (iounit) */ #define V9FS_IOHDRSZ 24 +struct v9fs_str { + u16 len; + char *str; +}; + /* qids are the unique ID for a file (like an inode */ struct v9fs_qid { u8 type; @@ -120,6 +130,29 @@ struct v9fs_stat { u32 atime; u32 mtime; u64 length; + struct v9fs_str name; + struct v9fs_str uid; + struct v9fs_str gid; + struct v9fs_str muid; + struct v9fs_str extension; /* 9p2000.u extensions */ + u32 n_uid; /* 9p2000.u extensions */ + u32 n_gid; /* 9p2000.u extensions */ + u32 n_muid; /* 9p2000.u extensions */ +}; + +/* file metadata (stat) structure used to create Twstat message + The is similar to v9fs_stat, but the strings don't point to + the same memory block and should be freed separately +*/ +struct v9fs_wstat { + u16 size; + u16 type; + u32 dev; + struct v9fs_qid qid; + u32 mode; + u32 atime; + u32 mtime; + u64 length; char *name; char *uid; char *gid; @@ -128,25 +161,24 @@ struct v9fs_stat { u32 n_uid; /* 9p2000.u extensions */ u32 n_gid; /* 9p2000.u extensions */ u32 n_muid; /* 9p2000.u extensions */ - char data[0]; }; /* Structures for Protocol Operations */ struct Tversion { u32 msize; - char *version; + struct v9fs_str version; }; struct Rversion { u32 msize; - char *version; + struct v9fs_str version; }; struct Tauth { u32 afid; - char *uname; - char *aname; + struct v9fs_str uname; + struct v9fs_str aname; }; struct Rauth { @@ -154,12 +186,12 @@ struct Rauth { }; struct Rerror { - char *error; + struct v9fs_str error; u32 errno; /* 9p2000.u extension */ }; struct Tflush { - u32 oldtag; + u16 oldtag; }; struct Rflush { @@ -168,8 +200,8 @@ struct Rflush { struct Tattach { u32 fid; u32 afid; - char *uname; - char *aname; + struct v9fs_str uname; + struct v9fs_str aname; }; struct Rattach { @@ -179,13 +211,13 @@ struct Rattach { struct Twalk { u32 fid; u32 newfid; - u32 nwname; - char **wnames; + u16 nwname; + struct v9fs_str wnames[16]; }; struct Rwalk { - u32 nwqid; - struct v9fs_qid *wqids; + u16 nwqid; + struct v9fs_qid wqids[16]; }; struct Topen { @@ -200,7 +232,7 @@ struct Ropen { struct Tcreate { u32 fid; - char *name; + struct v9fs_str name; u32 perm; u8 mode; }; @@ -251,12 +283,12 @@ struct Tstat { }; struct Rstat { - struct v9fs_stat *stat; + struct v9fs_stat stat; }; struct Twstat { u32 fid; - struct v9fs_stat *stat; + struct v9fs_stat stat; }; struct Rwstat { @@ -271,6 +303,7 @@ struct v9fs_fcall { u32 size; u8 id; u16 tag; + void *sdata; union { struct Tversion tversion; @@ -303,7 +336,9 @@ struct v9fs_fcall { } params; }; -#define FCALL_ERROR(fcall) (fcall ? fcall->params.rerror.error : "") +#define PRINT_FCALL_ERROR(s, fcall) dprintk(DEBUG_ERROR, "%s: %.*s\n", s, \ + fcall?fcall->params.rerror.error.len:0, \ + fcall?fcall->params.rerror.error.str:""); int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, char *version, struct v9fs_fcall **rcall); @@ -311,8 +346,7 @@ int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, int v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, u32 fid, u32 afid, struct v9fs_fcall **rcall); -int v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_fcall **rcall); +int v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid); int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 oldtag); @@ -320,7 +354,7 @@ int v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **rcall); int v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_stat *stat, struct v9fs_fcall **rcall); + struct v9fs_wstat *wstat, struct v9fs_fcall **rcall); int v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, char *name, struct v9fs_fcall **rcall); @@ -338,4 +372,5 @@ int v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset, u32 count, struct v9fs_fcall **rcall); int v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, - u32 count, void *data, struct v9fs_fcall **rcall); + u32 count, const char __user * data, + struct v9fs_fcall **rcall); diff --git a/fs/9p/Makefile b/fs/9p/Makefile index e4e4ffe..3d02308 100644 --- a/fs/9p/Makefile +++ b/fs/9p/Makefile @@ -1,17 +1,17 @@ obj-$(CONFIG_9P_FS) := 9p2000.o 9p2000-objs := \ + trans_fd.o \ + trans_sock.o \ + mux.o \ + 9p.o \ + conv.o \ vfs_super.o \ vfs_inode.o \ vfs_file.o \ vfs_dir.o \ vfs_dentry.o \ error.o \ - mux.o \ - trans_fd.o \ - trans_sock.o \ - 9p.o \ - conv.o \ v9fs.o \ fid.o diff --git a/fs/9p/conv.c b/fs/9p/conv.c index 18121af..55ccfa1 100644 --- a/fs/9p/conv.c +++ b/fs/9p/conv.c @@ -30,7 +30,7 @@ #include <linux/errno.h> #include <linux/fs.h> #include <linux/idr.h> - +#include <asm/uaccess.h> #include "debug.h" #include "v9fs.h" #include "9p.h" @@ -58,12 +58,15 @@ static inline int buf_check_overflow(struct cbuf *buf) static inline int buf_check_size(struct cbuf *buf, int len) { - if (buf->p+len > buf->ep) { + if (buf->p + len > buf->ep) { if (buf->p < buf->ep) { - eprintk(KERN_ERR, "buffer overflow\n"); + eprintk(KERN_ERR, "buffer overflow: want %d has %d\n", + len, (int)(buf->ep - buf->p)); + dump_stack(); buf->p = buf->ep + 1; - return 0; } + + return 0; } return 1; @@ -127,14 +130,6 @@ static inline void buf_put_string(struct cbuf *buf, const char *s) buf_put_stringn(buf, s, strlen(s)); } -static inline void buf_put_data(struct cbuf *buf, void *data, u32 datalen) -{ - if (buf_check_size(buf, datalen)) { - memcpy(buf->p, data, datalen); - buf->p += datalen; - } -} - static inline u8 buf_get_int8(struct cbuf *buf) { u8 ret = 0; @@ -183,86 +178,37 @@ static inline u64 buf_get_int64(struct cbuf *buf) return ret; } -static inline int -buf_get_string(struct cbuf *buf, char *data, unsigned int datalen) -{ - u16 len = 0; - - len = buf_get_int16(buf); - if (!buf_check_overflow(buf) && buf_check_size(buf, len) && len+1>datalen) { - memcpy(data, buf->p, len); - data[len] = 0; - buf->p += len; - len++; - } - - return len; -} - -static inline char *buf_get_stringb(struct cbuf *buf, struct cbuf *sbuf) -{ - char *ret; - u16 len; - - ret = NULL; - len = buf_get_int16(buf); - - if (!buf_check_overflow(buf) && buf_check_size(buf, len) && - buf_check_size(sbuf, len+1)) { - - memcpy(sbuf->p, buf->p, len); - sbuf->p[len] = 0; - ret = sbuf->p; - buf->p += len; - sbuf->p += len + 1; - } - - return ret; -} - -static inline int buf_get_data(struct cbuf *buf, void *data, int datalen) +static inline void buf_get_str(struct cbuf *buf, struct v9fs_str *vstr) { - int ret = 0; - - if (buf_check_size(buf, datalen)) { - memcpy(data, buf->p, datalen); - buf->p += datalen; - ret = datalen; + vstr->len = buf_get_int16(buf); + if (!buf_check_overflow(buf) && buf_check_size(buf, vstr->len)) { + vstr->str = buf->p; + buf->p += vstr->len; + } else { + vstr->len = 0; + vstr->str = NULL; } - - return ret; } -static inline void *buf_get_datab(struct cbuf *buf, struct cbuf *dbuf, - int datalen) +static inline void buf_get_qid(struct cbuf *bufp, struct v9fs_qid *qid) { - char *ret = NULL; - int n = 0; - - if (buf_check_size(dbuf, datalen)) { - n = buf_get_data(buf, dbuf->p, datalen); - if (n > 0) { - ret = dbuf->p; - dbuf->p += n; - } - } - - return ret; + qid->type = buf_get_int8(bufp); + qid->version = buf_get_int32(bufp); + qid->path = buf_get_int64(bufp); } /** - * v9fs_size_stat - calculate the size of a variable length stat struct - * @v9ses: session information + * v9fs_size_wstat - calculate the size of a variable length stat struct * @stat: metadata (stat) structure + * @extended: non-zero if 9P2000.u * */ -static int v9fs_size_stat(struct v9fs_session_info *v9ses, - struct v9fs_stat *stat) +static int v9fs_size_wstat(struct v9fs_wstat *wstat, int extended) { int size = 0; - if (stat == NULL) { + if (wstat == NULL) { eprintk(KERN_ERR, "v9fs_size_stat: got a NULL stat pointer\n"); return 0; } @@ -279,82 +225,38 @@ static int v9fs_size_stat(struct v9fs_session_info *v9ses, 8 + /* length[8] */ 8; /* minimum sum of string lengths */ - if (stat->name) - size += strlen(stat->name); - if (stat->uid) - size += strlen(stat->uid); - if (stat->gid) - size += strlen(stat->gid); - if (stat->muid) - size += strlen(stat->muid); + if (wstat->name) + size += strlen(wstat->name); + if (wstat->uid) + size += strlen(wstat->uid); + if (wstat->gid) + size += strlen(wstat->gid); + if (wstat->muid) + size += strlen(wstat->muid); - if (v9ses->extended) { + if (extended) { size += 4 + /* n_uid[4] */ 4 + /* n_gid[4] */ 4 + /* n_muid[4] */ 2; /* string length of extension[4] */ - if (stat->extension) - size += strlen(stat->extension); + if (wstat->extension) + size += strlen(wstat->extension); } return size; } /** - * serialize_stat - safely format a stat structure for transmission - * @v9ses: session info - * @stat: metadata (stat) structure - * @bufp: buffer to serialize structure into - * - */ - -static int -serialize_stat(struct v9fs_session_info *v9ses, struct v9fs_stat *stat, - struct cbuf *bufp) -{ - buf_put_int16(bufp, stat->size); - buf_put_int16(bufp, stat->type); - buf_put_int32(bufp, stat->dev); - buf_put_int8(bufp, stat->qid.type); - buf_put_int32(bufp, stat->qid.version); - buf_put_int64(bufp, stat->qid.path); - buf_put_int32(bufp, stat->mode); - buf_put_int32(bufp, stat->atime); - buf_put_int32(bufp, stat->mtime); - buf_put_int64(bufp, stat->length); - - buf_put_string(bufp, stat->name); - buf_put_string(bufp, stat->uid); - buf_put_string(bufp, stat->gid); - buf_put_string(bufp, stat->muid); - - if (v9ses->extended) { - buf_put_string(bufp, stat->extension); - buf_put_int32(bufp, stat->n_uid); - buf_put_int32(bufp, stat->n_gid); - buf_put_int32(bufp, stat->n_muid); - } - - if (buf_check_overflow(bufp)) - return 0; - - return stat->size; -} - -/** - * deserialize_stat - safely decode a recieved metadata (stat) structure - * @v9ses: session info + * buf_get_stat - safely decode a recieved metadata (stat) structure * @bufp: buffer to deserialize * @stat: metadata (stat) structure - * @dbufp: buffer to deserialize variable strings into + * @extended: non-zero if 9P2000.u * */ -static inline int -deserialize_stat(struct v9fs_session_info *v9ses, struct cbuf *bufp, - struct v9fs_stat *stat, struct cbuf *dbufp) +static inline void +buf_get_stat(struct cbuf *bufp, struct v9fs_stat *stat, int extended) { - stat->size = buf_get_int16(bufp); stat->type = buf_get_int16(bufp); stat->dev = buf_get_int32(bufp); @@ -365,282 +267,82 @@ deserialize_stat(struct v9fs_session_info *v9ses, struct cbuf *bufp, stat->atime = buf_get_int32(bufp); stat->mtime = buf_get_int32(bufp); stat->length = buf_get_int64(bufp); - stat->name = buf_get_stringb(bufp, dbufp); - stat->uid = buf_get_stringb(bufp, dbufp); - stat->gid = buf_get_stringb(bufp, dbufp); - stat->muid = buf_get_stringb(bufp, dbufp); + buf_get_str(bufp, &stat->name); + buf_get_str(bufp, &stat->uid); + buf_get_str(bufp, &stat->gid); + buf_get_str(bufp, &stat->muid); - if (v9ses->extended) { - stat->extension = buf_get_stringb(bufp, dbufp); + if (extended) { + buf_get_str(bufp, &stat->extension); stat->n_uid = buf_get_int32(bufp); stat->n_gid = buf_get_int32(bufp); stat->n_muid = buf_get_int32(bufp); } - - if (buf_check_overflow(bufp) || buf_check_overflow(dbufp)) - return 0; - - return stat->size + 2; -} - -/** - * deserialize_statb - wrapper for decoding a received metadata structure - * @v9ses: session info - * @bufp: buffer to deserialize - * @dbufp: buffer to deserialize variable strings into - * - */ - -static inline struct v9fs_stat *deserialize_statb(struct v9fs_session_info - *v9ses, struct cbuf *bufp, - struct cbuf *dbufp) -{ - struct v9fs_stat *ret = buf_alloc(dbufp, sizeof(struct v9fs_stat)); - - if (ret) { - int n = deserialize_stat(v9ses, bufp, ret, dbufp); - if (n <= 0) - return NULL; - } - - return ret; } /** * v9fs_deserialize_stat - decode a received metadata structure - * @v9ses: session info * @buf: buffer to deserialize * @buflen: length of received buffer * @stat: metadata structure to decode into - * @statlen: length of destination metadata structure + * @extended: non-zero if 9P2000.u * + * Note: stat will point to the buf region. */ int -v9fs_deserialize_stat(struct v9fs_session_info *v9ses, void *buf, - u32 buflen, struct v9fs_stat *stat, u32 statlen) +v9fs_deserialize_stat(void *buf, u32 buflen, struct v9fs_stat *stat, + int extended) { struct cbuf buffer; struct cbuf *bufp = &buffer; - struct cbuf dbuffer; - struct cbuf *dbufp = &dbuffer; + unsigned char *p; buf_init(bufp, buf, buflen); - buf_init(dbufp, (char *)stat + sizeof(struct v9fs_stat), - statlen - sizeof(struct v9fs_stat)); - - return deserialize_stat(v9ses, bufp, stat, dbufp); -} - -static inline int -v9fs_size_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall) -{ - int size = 4 + 1 + 2; /* size[4] msg[1] tag[2] */ - int i = 0; - - switch (fcall->id) { - default: - eprintk(KERN_ERR, "bad msg type %d\n", fcall->id); - return 0; - case TVERSION: /* msize[4] version[s] */ - size += 4 + 2 + strlen(fcall->params.tversion.version); - break; - case TAUTH: /* afid[4] uname[s] aname[s] */ - size += 4 + 2 + strlen(fcall->params.tauth.uname) + - 2 + strlen(fcall->params.tauth.aname); - break; - case TFLUSH: /* oldtag[2] */ - size += 2; - break; - case TATTACH: /* fid[4] afid[4] uname[s] aname[s] */ - size += 4 + 4 + 2 + strlen(fcall->params.tattach.uname) + - 2 + strlen(fcall->params.tattach.aname); - break; - case TWALK: /* fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ - size += 4 + 4 + 2; - /* now compute total for the array of names */ - for (i = 0; i < fcall->params.twalk.nwname; i++) - size += 2 + strlen(fcall->params.twalk.wnames[i]); - break; - case TOPEN: /* fid[4] mode[1] */ - size += 4 + 1; - break; - case TCREATE: /* fid[4] name[s] perm[4] mode[1] */ - size += 4 + 2 + strlen(fcall->params.tcreate.name) + 4 + 1; - break; - case TREAD: /* fid[4] offset[8] count[4] */ - size += 4 + 8 + 4; - break; - case TWRITE: /* fid[4] offset[8] count[4] data[count] */ - size += 4 + 8 + 4 + fcall->params.twrite.count; - break; - case TCLUNK: /* fid[4] */ - size += 4; - break; - case TREMOVE: /* fid[4] */ - size += 4; - break; - case TSTAT: /* fid[4] */ - size += 4; - break; - case TWSTAT: /* fid[4] stat[n] */ - fcall->params.twstat.stat->size = - v9fs_size_stat(v9ses, fcall->params.twstat.stat); - size += 4 + 2 + 2 + fcall->params.twstat.stat->size; - } - return size; -} - -/* - * v9fs_serialize_fcall - marshall fcall struct into a packet - * @v9ses: session information - * @fcall: structure to convert - * @data: buffer to serialize fcall into - * @datalen: length of buffer to serialize fcall into - * - */ - -int -v9fs_serialize_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall, - void *data, u32 datalen) -{ - int i = 0; - struct v9fs_stat *stat = NULL; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - buf_init(bufp, data, datalen); - - if (!fcall) { - eprintk(KERN_ERR, "no fcall\n"); - return -EINVAL; - } - - fcall->size = v9fs_size_fcall(v9ses, fcall); - - buf_put_int32(bufp, fcall->size); - buf_put_int8(bufp, fcall->id); - buf_put_int16(bufp, fcall->tag); - - dprintk(DEBUG_CONV, "size %d id %d tag %d\n", fcall->size, fcall->id, - fcall->tag); - - /* now encode it */ - switch (fcall->id) { - default: - eprintk(KERN_ERR, "bad msg type: %d\n", fcall->id); - return -EPROTO; - case TVERSION: - buf_put_int32(bufp, fcall->params.tversion.msize); - buf_put_string(bufp, fcall->params.tversion.version); - break; - case TAUTH: - buf_put_int32(bufp, fcall->params.tauth.afid); - buf_put_string(bufp, fcall->params.tauth.uname); - buf_put_string(bufp, fcall->params.tauth.aname); - break; - case TFLUSH: - buf_put_int16(bufp, fcall->params.tflush.oldtag); - break; - case TATTACH: - buf_put_int32(bufp, fcall->params.tattach.fid); - buf_put_int32(bufp, fcall->params.tattach.afid); - buf_put_string(bufp, fcall->params.tattach.uname); - buf_put_string(bufp, fcall->params.tattach.aname); - break; - case TWALK: - buf_put_int32(bufp, fcall->params.twalk.fid); - buf_put_int32(bufp, fcall->params.twalk.newfid); - buf_put_int16(bufp, fcall->params.twalk.nwname); - for (i = 0; i < fcall->params.twalk.nwname; i++) - buf_put_string(bufp, fcall->params.twalk.wnames[i]); - break; - case TOPEN: - buf_put_int32(bufp, fcall->params.topen.fid); - buf_put_int8(bufp, fcall->params.topen.mode); - break; - case TCREATE: - buf_put_int32(bufp, fcall->params.tcreate.fid); - buf_put_string(bufp, fcall->params.tcreate.name); - buf_put_int32(bufp, fcall->params.tcreate.perm); - buf_put_int8(bufp, fcall->params.tcreate.mode); - break; - case TREAD: - buf_put_int32(bufp, fcall->params.tread.fid); - buf_put_int64(bufp, fcall->params.tread.offset); - buf_put_int32(bufp, fcall->params.tread.count); - break; - case TWRITE: - buf_put_int32(bufp, fcall->params.twrite.fid); - buf_put_int64(bufp, fcall->params.twrite.offset); - buf_put_int32(bufp, fcall->params.twrite.count); - buf_put_data(bufp, fcall->params.twrite.data, - fcall->params.twrite.count); - break; - case TCLUNK: - buf_put_int32(bufp, fcall->params.tclunk.fid); - break; - case TREMOVE: - buf_put_int32(bufp, fcall->params.tremove.fid); - break; - case TSTAT: - buf_put_int32(bufp, fcall->params.tstat.fid); - break; - case TWSTAT: - buf_put_int32(bufp, fcall->params.twstat.fid); - stat = fcall->params.twstat.stat; - - buf_put_int16(bufp, stat->size + 2); - serialize_stat(v9ses, stat, bufp); - break; - } + p = bufp->p; + buf_get_stat(bufp, stat, extended); if (buf_check_overflow(bufp)) - return -EIO; - - return fcall->size; + return 0; + else + return bufp->p - p; } /** * deserialize_fcall - unmarshal a response - * @v9ses: session information - * @msgsize: size of rcall message * @buf: recieved buffer * @buflen: length of received buffer * @rcall: fcall structure to populate * @rcalllen: length of fcall structure to populate + * @extended: non-zero if 9P2000.u * */ int -v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize, - void *buf, u32 buflen, struct v9fs_fcall *rcall, - int rcalllen) +v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, + int extended) { struct cbuf buffer; struct cbuf *bufp = &buffer; - struct cbuf dbuffer; - struct cbuf *dbufp = &dbuffer; int i = 0; buf_init(bufp, buf, buflen); - buf_init(dbufp, (char *)rcall + sizeof(struct v9fs_fcall), - rcalllen - sizeof(struct v9fs_fcall)); - rcall->size = msgsize; + rcall->size = buf_get_int32(bufp); rcall->id = buf_get_int8(bufp); rcall->tag = buf_get_int16(bufp); dprintk(DEBUG_CONV, "size %d id %d tag %d\n", rcall->size, rcall->id, rcall->tag); + switch (rcall->id) { default: eprintk(KERN_ERR, "unknown message type: %d\n", rcall->id); return -EPROTO; case RVERSION: rcall->params.rversion.msize = buf_get_int32(bufp); - rcall->params.rversion.version = buf_get_stringb(bufp, dbufp); + buf_get_str(bufp, &rcall->params.rversion.version); break; case RFLUSH: break; @@ -651,34 +353,27 @@ v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize, break; case RWALK: rcall->params.rwalk.nwqid = buf_get_int16(bufp); - rcall->params.rwalk.wqids = buf_alloc(dbufp, - rcall->params.rwalk.nwqid * sizeof(struct v9fs_qid)); - if (rcall->params.rwalk.wqids) - for (i = 0; i < rcall->params.rwalk.nwqid; i++) { - rcall->params.rwalk.wqids[i].type = - buf_get_int8(bufp); - rcall->params.rwalk.wqids[i].version = - buf_get_int16(bufp); - rcall->params.rwalk.wqids[i].path = - buf_get_int64(bufp); - } + if (rcall->params.rwalk.nwqid > V9FS_MAXWELEM) { + eprintk(KERN_ERR, "Rwalk with more than %d qids: %d\n", + V9FS_MAXWELEM, rcall->params.rwalk.nwqid); + return -EPROTO; + } + + for (i = 0; i < rcall->params.rwalk.nwqid; i++) + buf_get_qid(bufp, &rcall->params.rwalk.wqids[i]); break; case ROPEN: - rcall->params.ropen.qid.type = buf_get_int8(bufp); - rcall->params.ropen.qid.version = buf_get_int32(bufp); - rcall->params.ropen.qid.path = buf_get_int64(bufp); + buf_get_qid(bufp, &rcall->params.ropen.qid); rcall->params.ropen.iounit = buf_get_int32(bufp); break; case RCREATE: - rcall->params.rcreate.qid.type = buf_get_int8(bufp); - rcall->params.rcreate.qid.version = buf_get_int32(bufp); - rcall->params.rcreate.qid.path = buf_get_int64(bufp); + buf_get_qid(bufp, &rcall->params.rcreate.qid); rcall->params.rcreate.iounit = buf_get_int32(bufp); break; case RREAD: rcall->params.rread.count = buf_get_int32(bufp); - rcall->params.rread.data = buf_get_datab(bufp, dbufp, - rcall->params.rread.count); + rcall->params.rread.data = bufp->p; + buf_check_size(bufp, rcall->params.rread.count); break; case RWRITE: rcall->params.rwrite.count = buf_get_int32(bufp); @@ -689,20 +384,443 @@ v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize, break; case RSTAT: buf_get_int16(bufp); - rcall->params.rstat.stat = - deserialize_statb(v9ses, bufp, dbufp); + buf_get_stat(bufp, &rcall->params.rstat.stat, extended); break; case RWSTAT: break; case RERROR: - rcall->params.rerror.error = buf_get_stringb(bufp, dbufp); - if (v9ses->extended) + buf_get_str(bufp, &rcall->params.rerror.error); + if (extended) rcall->params.rerror.errno = buf_get_int16(bufp); break; } - if (buf_check_overflow(bufp) || buf_check_overflow(dbufp)) + if (buf_check_overflow(bufp)) { + dprintk(DEBUG_ERROR, "buffer overflow\n"); return -EIO; + } + + return bufp->p - bufp->sp; +} + +static inline void v9fs_put_int8(struct cbuf *bufp, u8 val, u8 * p) +{ + *p = val; + buf_put_int8(bufp, val); +} + +static inline void v9fs_put_int16(struct cbuf *bufp, u16 val, u16 * p) +{ + *p = val; + buf_put_int16(bufp, val); +} + +static inline void v9fs_put_int32(struct cbuf *bufp, u32 val, u32 * p) +{ + *p = val; + buf_put_int32(bufp, val); +} + +static inline void v9fs_put_int64(struct cbuf *bufp, u64 val, u64 * p) +{ + *p = val; + buf_put_int64(bufp, val); +} - return rcall->size; +static inline void +v9fs_put_str(struct cbuf *bufp, char *data, struct v9fs_str *str) +{ + if (data) { + str->len = strlen(data); + str->str = bufp->p; + } else { + str->len = 0; + str->str = NULL; + } + + buf_put_stringn(bufp, data, str->len); +} + +static inline int +v9fs_put_user_data(struct cbuf *bufp, const char __user * data, int count, + unsigned char **pdata) +{ + *pdata = buf_alloc(bufp, count); + return copy_from_user(*pdata, data, count); +} + +static void +v9fs_put_wstat(struct cbuf *bufp, struct v9fs_wstat *wstat, + struct v9fs_stat *stat, int statsz, int extended) +{ + v9fs_put_int16(bufp, statsz, &stat->size); + v9fs_put_int16(bufp, wstat->type, &stat->type); + v9fs_put_int32(bufp, wstat->dev, &stat->dev); + v9fs_put_int8(bufp, wstat->qid.type, &stat->qid.type); + v9fs_put_int32(bufp, wstat->qid.version, &stat->qid.version); + v9fs_put_int64(bufp, wstat->qid.path, &stat->qid.path); + v9fs_put_int32(bufp, wstat->mode, &stat->mode); + v9fs_put_int32(bufp, wstat->atime, &stat->atime); + v9fs_put_int32(bufp, wstat->mtime, &stat->mtime); + v9fs_put_int64(bufp, wstat->length, &stat->length); + + v9fs_put_str(bufp, wstat->name, &stat->name); + v9fs_put_str(bufp, wstat->uid, &stat->uid); + v9fs_put_str(bufp, wstat->gid, &stat->gid); + v9fs_put_str(bufp, wstat->muid, &stat->muid); + + if (extended) { + v9fs_put_str(bufp, wstat->extension, &stat->extension); + v9fs_put_int32(bufp, wstat->n_uid, &stat->n_uid); + v9fs_put_int32(bufp, wstat->n_gid, &stat->n_gid); + v9fs_put_int32(bufp, wstat->n_muid, &stat->n_muid); + } +} + +static struct v9fs_fcall * +v9fs_create_common(struct cbuf *bufp, u32 size, u8 id) +{ + struct v9fs_fcall *fc; + + size += 4 + 1 + 2; /* size[4] id[1] tag[2] */ + fc = kmalloc(sizeof(struct v9fs_fcall) + size, GFP_KERNEL); + if (!fc) + return ERR_PTR(-ENOMEM); + + fc->sdata = (char *)fc + sizeof(*fc); + + buf_init(bufp, (char *)fc->sdata, size); + v9fs_put_int32(bufp, size, &fc->size); + v9fs_put_int8(bufp, id, &fc->id); + v9fs_put_int16(bufp, V9FS_NOTAG, &fc->tag); + + return fc; +} + +void v9fs_set_tag(struct v9fs_fcall *fc, u16 tag) +{ + fc->tag = tag; + *(__le16 *) (fc->sdata + 5) = cpu_to_le16(tag); +} + +struct v9fs_fcall *v9fs_create_tversion(u32 msize, char *version) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 2 + strlen(version); /* msize[4] version[s] */ + fc = v9fs_create_common(bufp, size, TVERSION); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, msize, &fc->params.tversion.msize); + v9fs_put_str(bufp, version, &fc->params.tversion.version); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tauth(u32 afid, char *uname, char *aname) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 2 + strlen(uname) + 2 + strlen(aname); /* afid[4] uname[s] aname[s] */ + fc = v9fs_create_common(bufp, size, TAUTH); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, afid, &fc->params.tauth.afid); + v9fs_put_str(bufp, uname, &fc->params.tauth.uname); + v9fs_put_str(bufp, aname, &fc->params.tauth.aname); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall * +v9fs_create_tattach(u32 fid, u32 afid, char *uname, char *aname) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 4 + 2 + strlen(uname) + 2 + strlen(aname); /* fid[4] afid[4] uname[s] aname[s] */ + fc = v9fs_create_common(bufp, size, TATTACH); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tattach.fid); + v9fs_put_int32(bufp, afid, &fc->params.tattach.afid); + v9fs_put_str(bufp, uname, &fc->params.tattach.uname); + v9fs_put_str(bufp, aname, &fc->params.tattach.aname); + + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tflush(u16 oldtag) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 2; /* oldtag[2] */ + fc = v9fs_create_common(bufp, size, TFLUSH); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int16(bufp, oldtag, &fc->params.tflush.oldtag); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_twalk(u32 fid, u32 newfid, u16 nwname, + char **wnames) +{ + int i, size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + if (nwname > V9FS_MAXWELEM) { + dprintk(DEBUG_ERROR, "nwname > %d\n", V9FS_MAXWELEM); + return NULL; + } + + size = 4 + 4 + 2; /* fid[4] newfid[4] nwname[2] ... */ + for (i = 0; i < nwname; i++) { + size += 2 + strlen(wnames[i]); /* wname[s] */ + } + + fc = v9fs_create_common(bufp, size, TWALK); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.twalk.fid); + v9fs_put_int32(bufp, newfid, &fc->params.twalk.newfid); + v9fs_put_int16(bufp, nwname, &fc->params.twalk.nwname); + for (i = 0; i < nwname; i++) { + v9fs_put_str(bufp, wnames[i], &fc->params.twalk.wnames[i]); + } + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_topen(u32 fid, u8 mode) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 1; /* fid[4] mode[1] */ + fc = v9fs_create_common(bufp, size, TOPEN); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.topen.fid); + v9fs_put_int8(bufp, mode, &fc->params.topen.mode); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tcreate(u32 fid, char *name, u32 perm, u8 mode) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 2 + strlen(name) + 4 + 1; /* fid[4] name[s] perm[4] mode[1] */ + fc = v9fs_create_common(bufp, size, TCREATE); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tcreate.fid); + v9fs_put_str(bufp, name, &fc->params.tcreate.name); + v9fs_put_int32(bufp, perm, &fc->params.tcreate.perm); + v9fs_put_int8(bufp, mode, &fc->params.tcreate.mode); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tread(u32 fid, u64 offset, u32 count) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 8 + 4; /* fid[4] offset[8] count[4] */ + fc = v9fs_create_common(bufp, size, TREAD); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tread.fid); + v9fs_put_int64(bufp, offset, &fc->params.tread.offset); + v9fs_put_int32(bufp, count, &fc->params.tread.count); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_twrite(u32 fid, u64 offset, u32 count, + const char __user * data) +{ + int size, err; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 8 + 4 + count; /* fid[4] offset[8] count[4] data[count] */ + fc = v9fs_create_common(bufp, size, TWRITE); + if (IS_ERR(fc)) + goto error; + + 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 (err) { + kfree(fc); + fc = ERR_PTR(err); + } + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tclunk(u32 fid) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4; /* fid[4] */ + fc = v9fs_create_common(bufp, size, TCLUNK); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tclunk.fid); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tremove(u32 fid) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4; /* fid[4] */ + fc = v9fs_create_common(bufp, size, TREMOVE); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tremove.fid); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tstat(u32 fid) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4; /* fid[4] */ + fc = v9fs_create_common(bufp, size, TSTAT); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tstat.fid); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_twstat(u32 fid, struct v9fs_wstat *wstat, + int extended) +{ + int size, statsz; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + statsz = v9fs_size_wstat(wstat, extended); + size = 4 + 2 + 2 + statsz; /* fid[4] stat[n] */ + fc = v9fs_create_common(bufp, size, TWSTAT); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.twstat.fid); + buf_put_int16(bufp, statsz + 2); + v9fs_put_wstat(bufp, wstat, &fc->params.twstat.stat, statsz, extended); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; } diff --git a/fs/9p/conv.h b/fs/9p/conv.h index ee84961..26a736e 100644 --- a/fs/9p/conv.h +++ b/fs/9p/conv.h @@ -1,8 +1,9 @@ /* * linux/fs/9p/conv.h * - * 9P protocol conversion definitions + * 9P protocol conversion definitions. * + * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> * @@ -24,13 +25,27 @@ * */ -int v9fs_deserialize_stat(struct v9fs_session_info *, void *buf, - u32 buflen, struct v9fs_stat *stat, u32 statlen); -int v9fs_serialize_fcall(struct v9fs_session_info *, struct v9fs_fcall *tcall, - void *buf, u32 buflen); -int v9fs_deserialize_fcall(struct v9fs_session_info *, u32 msglen, - void *buf, u32 buflen, struct v9fs_fcall *rcall, - int rcalllen); +int v9fs_deserialize_stat(void *buf, u32 buflen, struct v9fs_stat *stat, + int extended); +int v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, + int extended); -/* this one is actually in error.c right now */ -int v9fs_errstr2errno(char *errstr); +void v9fs_set_tag(struct v9fs_fcall *fc, u16 tag); + +struct v9fs_fcall *v9fs_create_tversion(u32 msize, char *version); +struct v9fs_fcall *v9fs_create_tauth(u32 afid, char *uname, char *aname); +struct v9fs_fcall *v9fs_create_tattach(u32 fid, u32 afid, char *uname, + char *aname); +struct v9fs_fcall *v9fs_create_tflush(u16 oldtag); +struct v9fs_fcall *v9fs_create_twalk(u32 fid, u32 newfid, u16 nwname, + char **wnames); +struct v9fs_fcall *v9fs_create_topen(u32 fid, u8 mode); +struct v9fs_fcall *v9fs_create_tcreate(u32 fid, char *name, u32 perm, u8 mode); +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); +struct v9fs_fcall *v9fs_create_tclunk(u32 fid); +struct v9fs_fcall *v9fs_create_tremove(u32 fid); +struct v9fs_fcall *v9fs_create_tstat(u32 fid); +struct v9fs_fcall *v9fs_create_twstat(u32 fid, struct v9fs_wstat *wstat, + int extended); diff --git a/fs/9p/debug.h b/fs/9p/debug.h index 4445f06..fe55103 100644 --- a/fs/9p/debug.h +++ b/fs/9p/debug.h @@ -51,16 +51,23 @@ do { \ #if DEBUG_DUMP_PKT static inline void dump_data(const unsigned char *data, unsigned int datalen) { - int i, j; - int len = datalen; + int i, n; + char buf[5*8]; - printk(KERN_DEBUG "data "); - for (i = 0; i < len; i += 4) { - for (j = 0; (j < 4) && (i + j < len); j++) - printk(KERN_DEBUG "%02x", data[i + j]); - printk(KERN_DEBUG " "); + n = 0; + i = 0; + while (i < datalen) { + n += snprintf(buf+n, sizeof(buf)-n, "%02x", data[i++]); + if (i%4 == 0) + n += snprintf(buf+n, sizeof(buf)-n, " "); + + if (i%16 == 0) { + dprintk(DEBUG_ERROR, "%s\n", buf); + n = 0; + } } - printk(KERN_DEBUG "\n"); + + dprintk(DEBUG_ERROR, "%s\n", buf); } #else /* DEBUG_DUMP_PKT */ static inline void dump_data(const unsigned char *data, unsigned int datalen) diff --git a/fs/9p/error.c b/fs/9p/error.c index 834cb17..e4b6f8f 100644 --- a/fs/9p/error.c +++ b/fs/9p/error.c @@ -33,7 +33,6 @@ #include <linux/list.h> #include <linux/jhash.h> -#include <linux/string.h> #include "debug.h" #include "error.h" @@ -55,7 +54,8 @@ int v9fs_error_init(void) /* load initial error map into hash table */ for (c = errmap; c->name != NULL; c++) { - bucket = jhash(c->name, strlen(c->name), 0) % ERRHASHSZ; + c->namelen = strlen(c->name); + bucket = jhash(c->name, c->namelen, 0) % ERRHASHSZ; INIT_HLIST_NODE(&c->list); hlist_add_head(&c->list, &hash_errmap[bucket]); } @@ -69,15 +69,15 @@ int v9fs_error_init(void) * */ -int v9fs_errstr2errno(char *errstr) +int v9fs_errstr2errno(char *errstr, int len) { int errno = 0; struct hlist_node *p = NULL; struct errormap *c = NULL; - int bucket = jhash(errstr, strlen(errstr), 0) % ERRHASHSZ; + int bucket = jhash(errstr, len, 0) % ERRHASHSZ; hlist_for_each_entry(c, p, &hash_errmap[bucket], list) { - if (!strcmp(c->name, errstr)) { + if (c->namelen==len && !memcmp(c->name, errstr, len)) { errno = c->val; break; } diff --git a/fs/9p/error.h b/fs/9p/error.h index 78f89ac..a9794e8 100644 --- a/fs/9p/error.h +++ b/fs/9p/error.h @@ -36,6 +36,7 @@ struct errormap { char *name; int val; + int namelen; struct hlist_node list; }; @@ -175,4 +176,3 @@ static struct errormap errmap[] = { }; extern int v9fs_error_init(void); -extern int v9fs_errstr2errno(char *errstr); diff --git a/fs/9p/fid.c b/fs/9p/fid.c index d95f862..eda4497 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -31,9 +31,6 @@ #include "v9fs.h" #include "9p.h" #include "v9fs_vfs.h" -#include "transport.h" -#include "mux.h" -#include "conv.h" #include "fid.h" /** @@ -164,7 +161,7 @@ static struct v9fs_fid *v9fs_fid_walk_up(struct dentry *dentry) return v9fs_fid_create(dentry, v9ses, fidnum, 0); clunk_fid: - v9fs_t_clunk(v9ses, fidnum, NULL); + v9fs_t_clunk(v9ses, fidnum); return ERR_PTR(err); } diff --git a/fs/9p/mux.c b/fs/9p/mux.c index 8835b57..945cb36 100644 --- a/fs/9p/mux.c +++ b/fs/9p/mux.c @@ -4,7 +4,7 @@ * Protocol Multiplexer * * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * Copyright (C) 2004 by Latchesar Ionkov <lucho@ionkov.net> + * Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,448 +28,943 @@ #include <linux/module.h> #include <linux/errno.h> #include <linux/fs.h> +#include <linux/poll.h> #include <linux/kthread.h> #include <linux/idr.h> #include "debug.h" #include "v9fs.h" #include "9p.h" -#include "transport.h" #include "conv.h" +#include "transport.h" #include "mux.h" +#define ERREQFLUSH 1 +#define SCHED_TIMEOUT 10 +#define MAXPOLLWADDR 2 + +enum { + Rworksched = 1, /* read work scheduled or running */ + Rpending = 2, /* can read */ + Wworksched = 4, /* write work scheduled or running */ + Wpending = 8, /* can write */ +}; + +struct v9fs_mux_poll_task; + +struct v9fs_req { + int tag; + struct v9fs_fcall *tcall; + struct v9fs_fcall *rcall; + int err; + v9fs_mux_req_callback cb; + void *cba; + struct list_head req_list; +}; + +struct v9fs_mux_data { + spinlock_t lock; + struct list_head mux_list; + struct v9fs_mux_poll_task *poll_task; + int msize; + unsigned char *extended; + struct v9fs_transport *trans; + struct v9fs_idpool tidpool; + int err; + wait_queue_head_t equeue; + struct list_head req_list; + struct list_head unsent_req_list; + struct v9fs_fcall *rcall; + int rpos; + char *rbuf; + int wpos; + int wsize; + char *wbuf; + wait_queue_t poll_wait[MAXPOLLWADDR]; + wait_queue_head_t *poll_waddr[MAXPOLLWADDR]; + poll_table pt; + struct work_struct rq; + struct work_struct wq; + unsigned long wsched; +}; + +struct v9fs_mux_poll_task { + struct task_struct *task; + struct list_head mux_list; + int muxnum; +}; + +struct v9fs_mux_rpc { + struct v9fs_mux_data *m; + struct v9fs_req *req; + int err; + struct v9fs_fcall *rcall; + wait_queue_head_t wqueue; +}; + +static int v9fs_poll_proc(void *); +static void v9fs_read_work(void *); +static void v9fs_write_work(void *); +static void v9fs_pollwait(struct file *filp, wait_queue_head_t * wait_address, + poll_table * p); +static u16 v9fs_mux_get_tag(struct v9fs_mux_data *); +static void v9fs_mux_put_tag(struct v9fs_mux_data *, u16); + +static DECLARE_MUTEX(v9fs_mux_task_lock); +static struct workqueue_struct *v9fs_mux_wq; + +static int v9fs_mux_num; +static int v9fs_mux_poll_task_num; +static struct v9fs_mux_poll_task v9fs_mux_poll_tasks[100]; + +int v9fs_mux_global_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(v9fs_mux_poll_tasks); i++) + v9fs_mux_poll_tasks[i].task = NULL; + + v9fs_mux_wq = create_workqueue("v9fs"); + if (!v9fs_mux_wq) + return -ENOMEM; + + return 0; +} + +void v9fs_mux_global_exit(void) +{ + destroy_workqueue(v9fs_mux_wq); +} + /** - * dprintcond - print condition of session info - * @v9ses: session info structure - * @req: RPC request structure + * v9fs_mux_calc_poll_procs - calculates the number of polling procs + * based on the number of mounted v9fs filesystems. * + * The current implementation returns sqrt of the number of mounts. */ +inline int v9fs_mux_calc_poll_procs(int muxnum) +{ + int n; + + if (v9fs_mux_poll_task_num) + n = muxnum / v9fs_mux_poll_task_num + + (muxnum % v9fs_mux_poll_task_num ? 1 : 0); + else + n = 1; + + if (n > ARRAY_SIZE(v9fs_mux_poll_tasks)) + n = ARRAY_SIZE(v9fs_mux_poll_tasks); + + return n; +} -static inline int -dprintcond(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) +static int v9fs_mux_poll_start(struct v9fs_mux_data *m) { - dprintk(DEBUG_MUX, "condition: %d, %p\n", v9ses->transport->status, - req->rcall); + int i, n; + struct v9fs_mux_poll_task *vpt, *vptlast; + struct task_struct *pproc; + + dprintk(DEBUG_MUX, "mux %p muxnum %d procnum %d\n", m, v9fs_mux_num, + v9fs_mux_poll_task_num); + up(&v9fs_mux_task_lock); + + n = v9fs_mux_calc_poll_procs(v9fs_mux_num + 1); + if (n > v9fs_mux_poll_task_num) { + for (i = 0; i < ARRAY_SIZE(v9fs_mux_poll_tasks); i++) { + if (v9fs_mux_poll_tasks[i].task == NULL) { + vpt = &v9fs_mux_poll_tasks[i]; + dprintk(DEBUG_MUX, "create proc %p\n", vpt); + pproc = kthread_create(v9fs_poll_proc, vpt, + "v9fs-poll"); + + if (!IS_ERR(pproc)) { + vpt->task = pproc; + INIT_LIST_HEAD(&vpt->mux_list); + vpt->muxnum = 0; + v9fs_mux_poll_task_num++; + wake_up_process(vpt->task); + } + break; + } + } + + if (i >= ARRAY_SIZE(v9fs_mux_poll_tasks)) + dprintk(DEBUG_ERROR, "warning: no free poll slots\n"); + } + + n = (v9fs_mux_num + 1) / v9fs_mux_poll_task_num + + ((v9fs_mux_num + 1) % v9fs_mux_poll_task_num ? 1 : 0); + + vptlast = NULL; + for (i = 0; i < ARRAY_SIZE(v9fs_mux_poll_tasks); i++) { + vpt = &v9fs_mux_poll_tasks[i]; + if (vpt->task != NULL) { + vptlast = vpt; + if (vpt->muxnum < n) { + dprintk(DEBUG_MUX, "put in proc %d\n", i); + list_add(&m->mux_list, &vpt->mux_list); + vpt->muxnum++; + m->poll_task = vpt; + memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); + init_poll_funcptr(&m->pt, v9fs_pollwait); + break; + } + } + } + + if (i >= ARRAY_SIZE(v9fs_mux_poll_tasks)) { + if (vptlast == NULL) + return -ENOMEM; + + dprintk(DEBUG_MUX, "put in proc %d\n", i); + list_add(&m->mux_list, &vptlast->mux_list); + vptlast->muxnum++; + m->poll_task = vptlast; + memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); + init_poll_funcptr(&m->pt, v9fs_pollwait); + } + + v9fs_mux_num++; + down(&v9fs_mux_task_lock); + return 0; } +static void v9fs_mux_poll_stop(struct v9fs_mux_data *m) +{ + int i; + struct v9fs_mux_poll_task *vpt; + + up(&v9fs_mux_task_lock); + vpt = m->poll_task; + list_del(&m->mux_list); + for(i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) { + if (m->poll_waddr[i] != NULL) { + remove_wait_queue(m->poll_waddr[i], &m->poll_wait[i]); + m->poll_waddr[i] = NULL; + } + } + vpt->muxnum--; + if (!vpt->muxnum) { + dprintk(DEBUG_MUX, "destroy proc %p\n", vpt); + send_sig(SIGKILL, vpt->task, 1); + vpt->task = NULL; + v9fs_mux_poll_task_num--; + } + v9fs_mux_num--; + down(&v9fs_mux_task_lock); +} + /** - * xread - force read of a certain number of bytes - * @v9ses: session info structure - * @ptr: pointer to buffer - * @sz: number of bytes to read + * v9fs_mux_init - allocate and initialize the per-session mux data + * Creates the polling task if this is the first session. * - * Chuck Cranor CS-533 project1 + * @trans - transport structure + * @msize - maximum message size + * @extended - pointer to the extended flag */ - -static int xread(struct v9fs_session_info *v9ses, void *ptr, unsigned long sz) +struct v9fs_mux_data *v9fs_mux_init(struct v9fs_transport *trans, int msize, + unsigned char *extended) { - int rd = 0; - int ret = 0; - while (rd < sz) { - ret = v9ses->transport->read(v9ses->transport, ptr, sz - rd); - if (ret <= 0) { - dprintk(DEBUG_ERROR, "xread errno %d\n", ret); - return ret; + int i, n; + struct v9fs_mux_data *m, *mtmp; + + dprintk(DEBUG_MUX, "transport %p msize %d\n", trans, msize); + m = kmalloc(sizeof(struct v9fs_mux_data), GFP_KERNEL); + if (!m) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&m->lock); + INIT_LIST_HEAD(&m->mux_list); + m->msize = msize; + m->extended = extended; + m->trans = trans; + idr_init(&m->tidpool.pool); + init_MUTEX(&m->tidpool.lock); + m->err = 0; + init_waitqueue_head(&m->equeue); + INIT_LIST_HEAD(&m->req_list); + INIT_LIST_HEAD(&m->unsent_req_list); + m->rcall = NULL; + m->rpos = 0; + m->rbuf = NULL; + m->wpos = m->wsize = 0; + m->wbuf = NULL; + INIT_WORK(&m->rq, v9fs_read_work, m); + INIT_WORK(&m->wq, v9fs_write_work, m); + m->wsched = 0; + memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); + m->poll_task = NULL; + n = v9fs_mux_poll_start(m); + if (n) + return ERR_PTR(n); + + n = trans->poll(trans, &m->pt); + if (n & POLLIN) { + dprintk(DEBUG_MUX, "mux %p can read\n", m); + set_bit(Rpending, &m->wsched); + } + + if (n & POLLOUT) { + dprintk(DEBUG_MUX, "mux %p can write\n", m); + set_bit(Wpending, &m->wsched); + } + + for(i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) { + if (IS_ERR(m->poll_waddr[i])) { + v9fs_mux_poll_stop(m); + mtmp = (void *)m->poll_waddr; /* the error code */ + kfree(m); + m = mtmp; + break; } - rd += ret; - ptr += ret; } - return (rd); + + return m; } /** - * read_message - read a full 9P2000 fcall packet - * @v9ses: session info structure - * @rcall: fcall structure to read into - * @rcalllen: size of fcall buffer - * + * v9fs_mux_destroy - cancels all pending requests and frees mux resources */ +void v9fs_mux_destroy(struct v9fs_mux_data *m) +{ + dprintk(DEBUG_MUX, "mux %p prev %p next %p\n", m, + m->mux_list.prev, m->mux_list.next); + v9fs_mux_cancel(m, -ECONNRESET); + + if (!list_empty(&m->req_list)) { + /* wait until all processes waiting on this session exit */ + dprintk(DEBUG_MUX, "mux %p waiting for empty request queue\n", + m); + wait_event_timeout(m->equeue, (list_empty(&m->req_list)), 5000); + dprintk(DEBUG_MUX, "mux %p request queue empty: %d\n", m, + list_empty(&m->req_list)); + } + + v9fs_mux_poll_stop(m); + m->trans = NULL; + + kfree(m); +} -static int -read_message(struct v9fs_session_info *v9ses, - struct v9fs_fcall *rcall, int rcalllen) +/** + * v9fs_pollwait - called by files poll operation to add v9fs-poll task + * to files wait queue + */ +static void +v9fs_pollwait(struct file *filp, wait_queue_head_t * wait_address, + poll_table * p) { - unsigned char buf[4]; - void *data; - int size = 0; - int res = 0; - - res = xread(v9ses, buf, sizeof(buf)); - if (res < 0) { - dprintk(DEBUG_ERROR, - "Reading of count field failed returned: %d\n", res); - return res; + int i; + struct v9fs_mux_data *m; + + m = container_of(p, struct v9fs_mux_data, pt); + for(i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) + if (m->poll_waddr[i] == NULL) + break; + + if (i >= ARRAY_SIZE(m->poll_waddr)) { + dprintk(DEBUG_ERROR, "not enough wait_address slots\n"); + return; } - if (res < 4) { - dprintk(DEBUG_ERROR, - "Reading of count field failed returned: %d\n", res); - return -EIO; + m->poll_waddr[i] = wait_address; + + if (!wait_address) { + dprintk(DEBUG_ERROR, "no wait_address\n"); + m->poll_waddr[i] = ERR_PTR(-EIO); + return; } - size = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - dprintk(DEBUG_MUX, "got a packet count: %d\n", size); + init_waitqueue_entry(&m->poll_wait[i], m->poll_task->task); + add_wait_queue(wait_address, &m->poll_wait[i]); +} + +/** + * v9fs_poll_mux - polls a mux and schedules read or write works if necessary + */ +static inline void v9fs_poll_mux(struct v9fs_mux_data *m) +{ + int n; - /* adjust for the four bytes of size */ - size -= 4; + if (m->err < 0) + return; - if (size > v9ses->maxdata) { - dprintk(DEBUG_ERROR, "packet too big: %d\n", size); - return -E2BIG; + n = m->trans->poll(m->trans, NULL); + if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) { + dprintk(DEBUG_MUX, "error mux %p err %d\n", m, n); + if (n >= 0) + n = -ECONNRESET; + v9fs_mux_cancel(m, n); } - data = kmalloc(size, GFP_KERNEL); - if (!data) { - eprintk(KERN_WARNING, "out of memory\n"); - return -ENOMEM; + if (n & POLLIN) { + set_bit(Rpending, &m->wsched); + dprintk(DEBUG_MUX, "mux %p can read\n", m); + if (!test_and_set_bit(Rworksched, &m->wsched)) { + dprintk(DEBUG_MUX, "schedule read work mux %p\n", m); + queue_work(v9fs_mux_wq, &m->rq); + } } - res = xread(v9ses, data, size); - if (res < size) { - dprintk(DEBUG_ERROR, "Reading of fcall failed returned: %d\n", - res); - kfree(data); - return res; + if (n & POLLOUT) { + set_bit(Wpending, &m->wsched); + dprintk(DEBUG_MUX, "mux %p can write\n", m); + if ((m->wsize || !list_empty(&m->unsent_req_list)) + && !test_and_set_bit(Wworksched, &m->wsched)) { + dprintk(DEBUG_MUX, "schedule write work mux %p\n", m); + queue_work(v9fs_mux_wq, &m->wq); + } } +} + +/** + * v9fs_poll_proc - polls all v9fs transports for new events and queues + * the appropriate work to the work queue + */ +static int v9fs_poll_proc(void *a) +{ + struct v9fs_mux_data *m, *mtmp; + struct v9fs_mux_poll_task *vpt; - /* we now have an in-memory string that is the reply. - * deserialize it. There is very little to go wrong at this point - * save for v9fs_alloc errors. - */ - res = v9fs_deserialize_fcall(v9ses, size, data, v9ses->maxdata, - rcall, rcalllen); + 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; - kfree(data); + list_for_each_entry_safe(m, mtmp, &vpt->mux_list, mux_list) { + v9fs_poll_mux(m); + } - if (res < 0) - return res; + dprintk(DEBUG_MUX, "sleeping...\n"); + schedule_timeout(SCHED_TIMEOUT * HZ); + } + __set_current_state(TASK_RUNNING); + dprintk(DEBUG_MUX, "finish\n"); return 0; } /** - * v9fs_recv - receive an RPC response for a particular tag - * @v9ses: session info structure - * @req: RPC request structure - * + * v9fs_write_work - called when a transport can send some data */ - -static int v9fs_recv(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) +static void v9fs_write_work(void *a) { - int ret = 0; + int n, err; + struct v9fs_mux_data *m; + struct v9fs_req *req; - dprintk(DEBUG_MUX, "waiting for response: %d\n", req->tcall->tag); - ret = wait_event_interruptible(v9ses->read_wait, - ((v9ses->transport->status != Connected) || - (req->rcall != 0) || (req->err < 0) || - dprintcond(v9ses, req))); + m = a; - dprintk(DEBUG_MUX, "got it: rcall %p\n", req->rcall); + if (m->err < 0) { + clear_bit(Wworksched, &m->wsched); + return; + } - spin_lock(&v9ses->muxlock); - list_del(&req->next); - spin_unlock(&v9ses->muxlock); + if (!m->wsize) { + if (list_empty(&m->unsent_req_list)) { + clear_bit(Wworksched, &m->wsched); + return; + } - if (req->err < 0) - return req->err; + spin_lock(&m->lock); + req = + list_entry(m->unsent_req_list.next, struct v9fs_req, + req_list); + list_move_tail(&req->req_list, &m->req_list); + m->wbuf = req->tcall->sdata; + m->wsize = req->tcall->size; + m->wpos = 0; + dump_data(m->wbuf, m->wsize); + spin_unlock(&m->lock); + } - if (v9ses->transport->status == Disconnected) - return -ECONNRESET; + dprintk(DEBUG_MUX, "mux %p pos %d size %d\n", m, m->wpos, m->wsize); + clear_bit(Wpending, &m->wsched); + err = m->trans->write(m->trans, m->wbuf + m->wpos, m->wsize - m->wpos); + dprintk(DEBUG_MUX, "mux %p sent %d bytes\n", m, err); + if (err == -EAGAIN) { + clear_bit(Wworksched, &m->wsched); + return; + } - return ret; -} + if (err <= 0) + goto error; -/** - * v9fs_send - send a 9P request - * @v9ses: session info structure - * @req: RPC request to send - * - */ + m->wpos += err; + if (m->wpos == m->wsize) + m->wpos = m->wsize = 0; + + if (m->wsize == 0 && !list_empty(&m->unsent_req_list)) { + if (test_and_clear_bit(Wpending, &m->wsched)) + n = POLLOUT; + else + n = m->trans->poll(m->trans, NULL); + + if (n & POLLOUT) { + dprintk(DEBUG_MUX, "schedule write work mux %p\n", m); + queue_work(v9fs_mux_wq, &m->wq); + } else + clear_bit(Wworksched, &m->wsched); + } else + clear_bit(Wworksched, &m->wsched); + + return; -static int v9fs_send(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) + error: + v9fs_mux_cancel(m, err); + clear_bit(Wworksched, &m->wsched); +} + +static void process_request(struct v9fs_mux_data *m, struct v9fs_req *req) { - int ret = -1; - void *data = NULL; - struct v9fs_fcall *tcall = req->tcall; + int ecode, tag; + struct v9fs_str *ename; - data = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL); - if (!data) - return -ENOMEM; + tag = req->tag; + if (req->rcall->id == RERROR && !req->err) { + ecode = req->rcall->params.rerror.errno; + ename = &req->rcall->params.rerror.error; - tcall->size = 0; /* enforce size recalculation */ - ret = - v9fs_serialize_fcall(v9ses, tcall, data, - v9ses->maxdata + V9FS_IOHDRSZ); - if (ret < 0) - goto free_data; + dprintk(DEBUG_MUX, "Rerror %.*s\n", ename->len, ename->str); - spin_lock(&v9ses->muxlock); - list_add(&req->next, &v9ses->mux_fcalls); - spin_unlock(&v9ses->muxlock); + if (*m->extended) + req->err = -ecode; - dprintk(DEBUG_MUX, "sending message: tag %d size %d\n", tcall->tag, - tcall->size); - ret = v9ses->transport->write(v9ses->transport, data, tcall->size); + if (!req->err) { + req->err = v9fs_errstr2errno(ename->str, ename->len); - if (ret != tcall->size) { - spin_lock(&v9ses->muxlock); - list_del(&req->next); - kfree(req->rcall); + if (!req->err) { /* string match failed */ + PRINT_FCALL_ERROR("unknown error", req->rcall); + } + + if (!req->err) + req->err = -ESERVERFAULT; + } + } else if (req->tcall && req->rcall->id != req->tcall->id + 1) { + dprintk(DEBUG_ERROR, "fcall mismatch: expected %d, got %d\n", + req->tcall->id + 1, req->rcall->id); + if (!req->err) + req->err = -EIO; + } - spin_unlock(&v9ses->muxlock); - if (ret >= 0) - ret = -EREMOTEIO; + if (req->cb && req->err != ERREQFLUSH) { + dprintk(DEBUG_MUX, "calling callback tcall %p rcall %p\n", + req->tcall, req->rcall); + + (*req->cb) (req->cba, req->tcall, req->rcall, req->err); + req->cb = NULL; } else - ret = 0; + kfree(req->rcall); - free_data: - kfree(data); - return ret; + v9fs_mux_put_tag(m, tag); + + wake_up(&m->equeue); + kfree(req); } /** - * v9fs_mux_rpc - send a request, receive a response - * @v9ses: session info structure - * @tcall: fcall to send - * @rcall: buffer to place response into - * + * v9fs_read_work - called when there is some data to be read from a transport */ - -long -v9fs_mux_rpc(struct v9fs_session_info *v9ses, struct v9fs_fcall *tcall, - struct v9fs_fcall **rcall) +static void v9fs_read_work(void *a) { - int tid = -1; - struct v9fs_fcall *fcall = NULL; - struct v9fs_rpcreq req; - int ret = -1; - - if (!v9ses) - return -EINVAL; - - if (!v9ses->transport || v9ses->transport->status != Connected) - return -EIO; + int n, err; + struct v9fs_mux_data *m; + struct v9fs_req *req, *rptr, *rreq; + struct v9fs_fcall *rcall; + char *rbuf; + + m = a; + + if (m->err < 0) + return; + + rcall = NULL; + dprintk(DEBUG_MUX, "start mux %p pos %d\n", m, m->rpos); + + if (!m->rcall) { + m->rcall = + kmalloc(sizeof(struct v9fs_fcall) + m->msize, GFP_KERNEL); + if (!m->rcall) { + err = -ENOMEM; + goto error; + } - if (rcall) - *rcall = NULL; + m->rbuf = (char *)m->rcall + sizeof(struct v9fs_fcall); + m->rpos = 0; + } - if (tcall->id != TVERSION) { - tid = v9fs_get_idpool(&v9ses->tidpool); - if (tid < 0) - return -ENOMEM; + clear_bit(Rpending, &m->wsched); + err = m->trans->read(m->trans, m->rbuf + m->rpos, m->msize - m->rpos); + dprintk(DEBUG_MUX, "mux %p got %d bytes\n", m, err); + if (err == -EAGAIN) { + clear_bit(Rworksched, &m->wsched); + return; } - tcall->tag = tid; + if (err <= 0) + goto error; - req.tcall = tcall; - req.err = 0; - req.rcall = NULL; + m->rpos += err; + while (m->rpos > 4) { + n = le32_to_cpu(*(__le32 *) m->rbuf); + if (n >= m->msize) { + dprintk(DEBUG_ERROR, + "requested packet size too big: %d\n", n); + err = -EIO; + goto error; + } - ret = v9fs_send(v9ses, &req); + if (m->rpos < n) + break; - if (ret < 0) { - if (tcall->id != TVERSION) - v9fs_put_idpool(tid, &v9ses->tidpool); - dprintk(DEBUG_MUX, "error %d\n", ret); - return ret; - } + dump_data(m->rbuf, n); + err = + v9fs_deserialize_fcall(m->rbuf, n, m->rcall, *m->extended); + if (err < 0) { + goto error; + } + + rcall = m->rcall; + rbuf = m->rbuf; + if (m->rpos > n) { + m->rcall = kmalloc(sizeof(struct v9fs_fcall) + m->msize, + GFP_KERNEL); + if (!m->rcall) { + err = -ENOMEM; + goto error; + } - ret = v9fs_recv(v9ses, &req); - - fcall = req.rcall; - - dprintk(DEBUG_MUX, "received: tag=%x, ret=%d\n", tcall->tag, ret); - if (ret == -ERESTARTSYS) { - if (v9ses->transport->status != Disconnected - && tcall->id != TFLUSH) { - unsigned long flags; - - dprintk(DEBUG_MUX, "flushing the tag: %d\n", - tcall->tag); - clear_thread_flag(TIF_SIGPENDING); - v9fs_t_flush(v9ses, tcall->tag); - spin_lock_irqsave(¤t->sighand->siglock, flags); - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, - flags); - dprintk(DEBUG_MUX, "flushing done\n"); + m->rbuf = (char *)m->rcall + sizeof(struct v9fs_fcall); + memmove(m->rbuf, rbuf + n, m->rpos - n); + m->rpos -= n; + } else { + m->rcall = NULL; + m->rbuf = NULL; + m->rpos = 0; } - goto release_req; - } else if (ret < 0) - goto release_req; - - if (!fcall) - ret = -EIO; - else { - if (fcall->id == RERROR) { - ret = v9fs_errstr2errno(fcall->params.rerror.error); - if (ret == 0) { /* string match failed */ - if (fcall->params.rerror.errno) - ret = -(fcall->params.rerror.errno); - else - ret = -ESERVERFAULT; + dprintk(DEBUG_MUX, "mux %p fcall id %d tag %d\n", m, rcall->id, + rcall->tag); + + req = NULL; + spin_lock(&m->lock); + list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) { + if (rreq->tag == rcall->tag) { + req = rreq; + req->rcall = rcall; + list_del(&req->req_list); + spin_unlock(&m->lock); + process_request(m, req); + break; } - } else if (fcall->id != tcall->id + 1) { - dprintk(DEBUG_ERROR, - "fcall mismatch: expected %d, got %d\n", - tcall->id + 1, fcall->id); - ret = -EIO; + + } + + if (!req) { + spin_unlock(&m->lock); + if (err >= 0 && rcall->id != RFLUSH) + dprintk(DEBUG_ERROR, + "unexpected response mux %p id %d tag %d\n", + m, rcall->id, rcall->tag); + kfree(rcall); } } - release_req: - if (tcall->id != TVERSION) - v9fs_put_idpool(tid, &v9ses->tidpool); - if (rcall) - *rcall = fcall; - else - kfree(fcall); + if (!list_empty(&m->req_list)) { + if (test_and_clear_bit(Rpending, &m->wsched)) + n = POLLIN; + else + n = m->trans->poll(m->trans, NULL); + + if (n & POLLIN) { + dprintk(DEBUG_MUX, "schedule read work mux %p\n", m); + queue_work(v9fs_mux_wq, &m->rq); + } else + clear_bit(Rworksched, &m->wsched); + } else + clear_bit(Rworksched, &m->wsched); + + return; - return ret; + error: + v9fs_mux_cancel(m, err); + clear_bit(Rworksched, &m->wsched); } /** - * v9fs_mux_cancel_requests - cancels all pending requests + * v9fs_send_request - send 9P request + * The function can sleep until the request is scheduled for sending. + * The function can be interrupted. Return from the function is not + * a guarantee that the request is sent succesfully. Can return errors + * that can be retrieved by PTR_ERR macros. * - * @v9ses: session info structure - * @err: error code to return to the requests + * @m: mux data + * @tc: request to be sent + * @cb: callback function to call when response is received + * @cba: parameter to pass to the callback function */ -void v9fs_mux_cancel_requests(struct v9fs_session_info *v9ses, int err) +static struct v9fs_req *v9fs_send_request(struct v9fs_mux_data *m, + struct v9fs_fcall *tc, + v9fs_mux_req_callback cb, void *cba) { - struct v9fs_rpcreq *rptr; - struct v9fs_rpcreq *rreq; + int n; + struct v9fs_req *req; - dprintk(DEBUG_MUX, " %d\n", err); - spin_lock(&v9ses->muxlock); - list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) { - rreq->err = err; - } - spin_unlock(&v9ses->muxlock); - wake_up_all(&v9ses->read_wait); -} + dprintk(DEBUG_MUX, "mux %p task %p tcall %p id %d\n", m, current, + tc, tc->id); + if (m->err < 0) + return ERR_PTR(m->err); -/** - * v9fs_recvproc - kproc to handle demultiplexing responses - * @data: session info structure - * - */ + req = kmalloc(sizeof(struct v9fs_req), GFP_KERNEL); + if (!req) + return ERR_PTR(-ENOMEM); -static int v9fs_recvproc(void *data) -{ - struct v9fs_session_info *v9ses = (struct v9fs_session_info *)data; - struct v9fs_fcall *rcall = NULL; - struct v9fs_rpcreq *rptr; - struct v9fs_rpcreq *req; - struct v9fs_rpcreq *rreq; - int err = 0; + if (tc->id == TVERSION) + n = V9FS_NOTAG; + else + n = v9fs_mux_get_tag(m); - allow_signal(SIGKILL); - set_current_state(TASK_INTERRUPTIBLE); - complete(&v9ses->proccmpl); - while (!kthread_should_stop() && err >= 0) { - req = rptr = rreq = NULL; - - rcall = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL); - if (!rcall) { - eprintk(KERN_ERR, "no memory for buffers\n"); - break; - } + if (n < 0) + return ERR_PTR(-ENOMEM); - err = read_message(v9ses, rcall, v9ses->maxdata + V9FS_IOHDRSZ); - spin_lock(&v9ses->muxlock); - if (err < 0) { - list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) { - rreq->err = err; - } - if(err != -ERESTARTSYS) - eprintk(KERN_ERR, - "Transport error while reading message %d\n", err); - } else { - list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) { - if (rreq->tcall->tag == rcall->tag) { - req = rreq; - req->rcall = rcall; - break; - } - } - } + v9fs_set_tag(tc, n); - if (req && (req->tcall->id == TFLUSH)) { - struct v9fs_rpcreq *treq = NULL; - list_for_each_entry_safe(treq, rptr, &v9ses->mux_fcalls, next) { - if (treq->tcall->tag == - req->tcall->params.tflush.oldtag) { - list_del(&rptr->next); - kfree(treq->rcall); - break; - } + req->tag = n; + req->tcall = tc; + req->rcall = NULL; + req->err = 0; + req->cb = cb; + req->cba = cba; + + spin_lock(&m->lock); + list_add_tail(&req->req_list, &m->unsent_req_list); + spin_unlock(&m->lock); + + if (test_and_clear_bit(Wpending, &m->wsched)) + n = POLLOUT; + else + n = m->trans->poll(m->trans, NULL); + + if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched)) + queue_work(v9fs_mux_wq, &m->wq); + + return req; +} + +static inline void +v9fs_mux_flush_cb(void *a, struct v9fs_fcall *tc, struct v9fs_fcall *rc, + int err) +{ + v9fs_mux_req_callback cb; + int tag; + struct v9fs_mux_data *m; + struct v9fs_req *req, *rptr; + + m = a; + dprintk(DEBUG_MUX, "mux %p tc %p rc %p err %d oldtag %d\n", m, tc, + rc, err, tc->params.tflush.oldtag); + + spin_lock(&m->lock); + cb = NULL; + tag = tc->params.tflush.oldtag; + list_for_each_entry_safe(req, rptr, &m->req_list, req_list) { + if (req->tag == tag) { + list_del(&req->req_list); + if (req->cb) { + cb = req->cb; + req->cb = NULL; + spin_unlock(&m->lock); + (*cb) (req->cba, req->tcall, req->rcall, + req->err); } + kfree(req); + wake_up(&m->equeue); + break; } + } - spin_unlock(&v9ses->muxlock); + if (!cb) + spin_unlock(&m->lock); - if (!req) { - if (err >= 0) - dprintk(DEBUG_ERROR, - "unexpected response: id %d tag %d\n", - rcall->id, rcall->tag); + v9fs_mux_put_tag(m, tag); + kfree(tc); + kfree(rc); +} - kfree(rcall); - } +static void +v9fs_mux_flush_request(struct v9fs_mux_data *m, struct v9fs_req *req) +{ + struct v9fs_fcall *fc; - wake_up_all(&v9ses->read_wait); - set_current_state(TASK_INTERRUPTIBLE); + dprintk(DEBUG_MUX, "mux %p req %p tag %d\n", m, req, req->tag); + + fc = v9fs_create_tflush(req->tag); + v9fs_send_request(m, fc, v9fs_mux_flush_cb, m); +} + +static void +v9fs_mux_rpc_cb(void *a, struct v9fs_fcall *tc, struct v9fs_fcall *rc, int err) +{ + struct v9fs_mux_rpc *r; + + if (err == ERREQFLUSH) { + dprintk(DEBUG_MUX, "err req flush\n"); + return; } - v9ses->transport->close(v9ses->transport); + r = a; + dprintk(DEBUG_MUX, "mux %p req %p tc %p rc %p err %d\n", r->m, r->req, + tc, rc, err); + r->rcall = rc; + r->err = err; + wake_up(&r->wqueue); +} - /* Inform all pending processes about the failure */ - wake_up_all(&v9ses->read_wait); +/** + * v9fs_mux_rpc - sends 9P request and waits until a response is available. + * The function can be interrupted. + * @m: mux data + * @tc: request to be sent + * @rc: pointer where a pointer to the response is stored + */ +int +v9fs_mux_rpc(struct v9fs_mux_data *m, struct v9fs_fcall *tc, + struct v9fs_fcall **rc) +{ + int err; + unsigned long flags; + struct v9fs_req *req; + struct v9fs_mux_rpc r; + + r.err = 0; + r.rcall = NULL; + r.m = m; + init_waitqueue_head(&r.wqueue); + + if (rc) + *rc = NULL; + + req = v9fs_send_request(m, tc, v9fs_mux_rpc_cb, &r); + if (IS_ERR(req)) { + err = PTR_ERR(req); + dprintk(DEBUG_MUX, "error %d\n", err); + return PTR_ERR(req); + } - if (signal_pending(current)) - complete(&v9ses->proccmpl); + r.req = req; + dprintk(DEBUG_MUX, "mux %p tc %p tag %d rpc %p req %p\n", m, tc, + req->tag, &r, req); + err = wait_event_interruptible(r.wqueue, r.rcall != NULL || r.err < 0); + if (r.err < 0) + err = r.err; + + if (err == -ERESTARTSYS && m->trans->status == Connected && m->err == 0) { + spin_lock(&m->lock); + req->tcall = NULL; + req->err = ERREQFLUSH; + spin_unlock(&m->lock); + + clear_thread_flag(TIF_SIGPENDING); + v9fs_mux_flush_request(m, req); + spin_lock_irqsave(¤t->sighand->siglock, flags); + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); + } - dprintk(DEBUG_MUX, "recvproc: end\n"); - v9ses->recvproc = NULL; + if (!err) { + if (r.rcall) + dprintk(DEBUG_MUX, "got response id %d tag %d\n", + r.rcall->id, r.rcall->tag); + + if (rc) + *rc = r.rcall; + else + kfree(r.rcall); + } else { + kfree(r.rcall); + dprintk(DEBUG_MUX, "got error %d\n", err); + if (err > 0) + err = -EIO; + } - return err >= 0; + return err; } /** - * v9fs_mux_init - initialize multiplexer (spawn kproc) - * @v9ses: session info structure - * @dev_name: mount device information (to create unique kproc) - * + * v9fs_mux_rpcnb - sends 9P request without waiting for response. + * @m: mux data + * @tc: request to be sent + * @cb: callback function to be called when response arrives + * @cba: value to pass to the callback function */ +int v9fs_mux_rpcnb(struct v9fs_mux_data *m, struct v9fs_fcall *tc, + v9fs_mux_req_callback cb, void *a) +{ + int err; + struct v9fs_req *req; + + req = v9fs_send_request(m, tc, cb, a); + if (IS_ERR(req)) { + err = PTR_ERR(req); + dprintk(DEBUG_MUX, "error %d\n", err); + return PTR_ERR(req); + } + + dprintk(DEBUG_MUX, "mux %p tc %p tag %d\n", m, tc, req->tag); + return 0; +} -int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name) +/** + * v9fs_mux_cancel - cancel all pending requests with error + * @m: mux data + * @err: error code + */ +void v9fs_mux_cancel(struct v9fs_mux_data *m, int err) { - char procname[60]; - - strncpy(procname, dev_name, sizeof(procname)); - procname[sizeof(procname) - 1] = 0; - - init_waitqueue_head(&v9ses->read_wait); - init_completion(&v9ses->fcread); - init_completion(&v9ses->proccmpl); - spin_lock_init(&v9ses->muxlock); - INIT_LIST_HEAD(&v9ses->mux_fcalls); - v9ses->recvproc = NULL; - v9ses->curfcall = NULL; - - v9ses->recvproc = kthread_create(v9fs_recvproc, v9ses, - "v9fs_recvproc %s", procname); - - if (IS_ERR(v9ses->recvproc)) { - eprintk(KERN_ERR, "cannot create receiving thread\n"); - v9fs_session_close(v9ses); - return -ECONNABORTED; + struct v9fs_req *req, *rtmp; + LIST_HEAD(cancel_list); + + dprintk(DEBUG_MUX, "mux %p err %d\n", m, err); + m->err = err; + spin_lock(&m->lock); + list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) { + list_move(&req->req_list, &cancel_list); } + spin_unlock(&m->lock); - wake_up_process(v9ses->recvproc); - wait_for_completion(&v9ses->proccmpl); + list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) { + list_del(&req->req_list); + if (!req->err) + req->err = err; - return 0; + if (req->cb) + (*req->cb) (req->cba, req->tcall, req->rcall, req->err); + else + kfree(req->rcall); + + kfree(req); + } + + wake_up(&m->equeue); +} + +static u16 v9fs_mux_get_tag(struct v9fs_mux_data *m) +{ + int tag; + + tag = v9fs_get_idpool(&m->tidpool); + if (tag < 0) + return V9FS_NOTAG; + else + return (u16) tag; +} + +static void v9fs_mux_put_tag(struct v9fs_mux_data *m, u16 tag) +{ + if (tag != V9FS_NOTAG && v9fs_check_idpool(tag, &m->tidpool)) + v9fs_put_idpool(tag, &m->tidpool); } diff --git a/fs/9p/mux.h b/fs/9p/mux.h index 4994cb1..9473b84 100644 --- a/fs/9p/mux.h +++ b/fs/9p/mux.h @@ -3,6 +3,7 @@ * * Multiplexer Definitions * + * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> * * This program is free software; you can redistribute it and/or modify @@ -23,19 +24,35 @@ * */ -/* structure to manage each RPC transaction */ +struct v9fs_mux_data; -struct v9fs_rpcreq { - struct v9fs_fcall *tcall; - struct v9fs_fcall *rcall; - int err; /* error code if response failed */ +/** + * v9fs_mux_req_callback - callback function that is called when the + * response of a request is received. The callback is called from + * a workqueue and shouldn't block. + * + * @a - the pointer that was specified when the request was send to be + * passed to the callback + * @tc - request call + * @rc - response call + * @err - error code (non-zero if error occured) + */ +typedef void (*v9fs_mux_req_callback)(void *a, struct v9fs_fcall *tc, + struct v9fs_fcall *rc, int err); + +int v9fs_mux_global_init(void); +void v9fs_mux_global_exit(void); - /* XXX - could we put scatter/gather buffers here? */ +struct v9fs_mux_data *v9fs_mux_init(struct v9fs_transport *trans, int msize, + unsigned char *extended); +void v9fs_mux_destroy(struct v9fs_mux_data *); - struct list_head next; -}; +int v9fs_mux_send(struct v9fs_mux_data *m, struct v9fs_fcall *tc); +struct v9fs_fcall *v9fs_mux_recv(struct v9fs_mux_data *m); +int v9fs_mux_rpc(struct v9fs_mux_data *m, struct v9fs_fcall *tc, struct v9fs_fcall **rc); +int v9fs_mux_rpcnb(struct v9fs_mux_data *m, struct v9fs_fcall *tc, + v9fs_mux_req_callback cb, void *a); -int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name); -long v9fs_mux_rpc(struct v9fs_session_info *v9ses, - struct v9fs_fcall *tcall, struct v9fs_fcall **rcall); -void v9fs_mux_cancel_requests(struct v9fs_session_info *v9ses, int err); +void v9fs_mux_flush(struct v9fs_mux_data *m, int sendflush); +void v9fs_mux_cancel(struct v9fs_mux_data *m, int err); +int v9fs_errstr2errno(char *errstr, int len); diff --git a/fs/9p/trans_fd.c b/fs/9p/trans_fd.c index 63b58ce..1a28ef9 100644 --- a/fs/9p/trans_fd.c +++ b/fs/9p/trans_fd.c @@ -3,6 +3,7 @@ * * File Descriptor Transport Layer * + * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> * Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com> * * This program is free software; you can redistribute it and/or modify @@ -106,9 +107,6 @@ v9fs_fd_init(struct v9fs_session_info *v9ses, const char *addr, char *data) return -ENOPROTOOPT; } - sema_init(&trans->writelock, 1); - sema_init(&trans->readlock, 1); - ts = kmalloc(sizeof(struct v9fs_trans_fd), GFP_KERNEL); if (!ts) @@ -148,12 +146,12 @@ static void v9fs_fd_close(struct v9fs_transport *trans) if (!trans) return; - trans->status = Disconnected; - ts = trans->priv; + ts = xchg(&trans->priv, NULL); if (!ts) return; + trans->status = Disconnected; if (ts->in_file) fput(ts->in_file); @@ -163,10 +161,55 @@ static void v9fs_fd_close(struct v9fs_transport *trans) kfree(ts); } +static unsigned int +v9fs_fd_poll(struct v9fs_transport *trans, struct poll_table_struct *pt) +{ + int ret, n; + struct v9fs_trans_fd *ts; + mm_segment_t oldfs; + + if (!trans) + return -EIO; + + ts = trans->priv; + if (trans->status != Connected || !ts) + return -EIO; + + oldfs = get_fs(); + set_fs(get_ds()); + + if (!ts->in_file->f_op || !ts->in_file->f_op->poll) { + ret = -EIO; + goto end; + } + + ret = ts->in_file->f_op->poll(ts->in_file, pt); + + if (ts->out_file != ts->in_file) { + if (!ts->out_file->f_op || !ts->out_file->f_op->poll) { + ret = -EIO; + goto end; + } + + n = ts->out_file->f_op->poll(ts->out_file, pt); + + ret &= ~POLLOUT; + n &= ~POLLIN; + + ret |= n; + } + +end: + set_fs(oldfs); + return ret; +} + + struct v9fs_transport v9fs_trans_fd = { .init = v9fs_fd_init, .write = v9fs_fd_send, .read = v9fs_fd_recv, .close = v9fs_fd_close, + .poll = v9fs_fd_poll, }; diff --git a/fs/9p/trans_sock.c b/fs/9p/trans_sock.c index 6a9a75d..44e8306 100644 --- a/fs/9p/trans_sock.c +++ b/fs/9p/trans_sock.c @@ -3,6 +3,7 @@ * * Socket Transport Layer * + * Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net> * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> * Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com> * Copyright (C) 1995, 1996 by Olaf Kirch <okir@monad.swb.de> @@ -36,6 +37,7 @@ #include <asm/uaccess.h> #include <linux/inet.h> #include <linux/idr.h> +#include <linux/file.h> #include "debug.h" #include "v9fs.h" @@ -45,6 +47,7 @@ struct v9fs_trans_sock { struct socket *s; + struct file *filp; }; /** @@ -57,41 +60,26 @@ struct v9fs_trans_sock { static int v9fs_sock_recv(struct v9fs_transport *trans, void *v, int len) { - struct msghdr msg; - struct kvec iov; - int result; - mm_segment_t oldfs; - struct v9fs_trans_sock *ts = trans ? trans->priv : NULL; + int ret; + struct v9fs_trans_sock *ts; - if (trans->status == Disconnected) + if (!trans || trans->status == Disconnected) { + dprintk(DEBUG_ERROR, "disconnected ...\n"); return -EREMOTEIO; + } - result = -EINVAL; - - oldfs = get_fs(); - set_fs(get_ds()); - - iov.iov_base = v; - iov.iov_len = len; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_namelen = 0; - msg.msg_flags = MSG_NOSIGNAL; + ts = trans->priv; - result = kernel_recvmsg(ts->s, &msg, &iov, 1, len, 0); + if (!(ts->filp->f_flags & O_NONBLOCK)) + dprintk(DEBUG_ERROR, "blocking read ...\n"); - dprintk(DEBUG_TRANS, "socket state %d\n", ts->s->state); - set_fs(oldfs); - - if (result <= 0) { - if (result != -ERESTARTSYS) + ret = kernel_read(ts->filp, ts->filp->f_pos, v, len); + if (ret <= 0) { + if (ret != -ERESTARTSYS && ret != -EAGAIN) trans->status = Disconnected; } - return result; + return ret; } /** @@ -104,40 +92,72 @@ static int v9fs_sock_recv(struct v9fs_transport *trans, void *v, int len) static int v9fs_sock_send(struct v9fs_transport *trans, void *v, int len) { - struct kvec iov; - struct msghdr msg; - int result = -1; + int ret; mm_segment_t oldfs; - struct v9fs_trans_sock *ts = trans ? trans->priv : NULL; + struct v9fs_trans_sock *ts; - dprintk(DEBUG_TRANS, "Sending packet size %d (%x)\n", len, len); - dump_data(v, len); + if (!trans || trans->status == Disconnected) { + dprintk(DEBUG_ERROR, "disconnected ...\n"); + return -EREMOTEIO; + } + + ts = trans->priv; + if (!ts) { + dprintk(DEBUG_ERROR, "no transport ...\n"); + return -EREMOTEIO; + } - down(&trans->writelock); + if (!(ts->filp->f_flags & O_NONBLOCK)) + dprintk(DEBUG_ERROR, "blocking write ...\n"); oldfs = get_fs(); set_fs(get_ds()); - iov.iov_base = v; - iov.iov_len = len; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_namelen = 0; - msg.msg_flags = MSG_NOSIGNAL; - result = kernel_sendmsg(ts->s, &msg, &iov, 1, len); + ret = vfs_write(ts->filp, (void __user *)v, len, &ts->filp->f_pos); set_fs(oldfs); - if (result < 0) { - if (result != -ERESTARTSYS) + if (ret < 0) { + if (ret != -ERESTARTSYS) trans->status = Disconnected; } - up(&trans->writelock); - return result; + return ret; +} + +static unsigned int v9fs_sock_poll(struct v9fs_transport *trans, + struct poll_table_struct *pt) { + + int ret; + struct v9fs_trans_sock *ts; + mm_segment_t oldfs; + + if (!trans) { + dprintk(DEBUG_ERROR, "no transport\n"); + return -EIO; + } + + ts = trans->priv; + if (trans->status != Connected || !ts) { + dprintk(DEBUG_ERROR, "transport disconnected: %d\n", trans->status); + return -EIO; + } + + oldfs = get_fs(); + set_fs(get_ds()); + + if (!ts->filp->f_op || !ts->filp->f_op->poll) { + dprintk(DEBUG_ERROR, "no poll operation\n"); + ret = -EIO; + goto end; + } + + ret = ts->filp->f_op->poll(ts->filp, pt); + +end: + set_fs(oldfs); + return ret; } + /** * v9fs_tcp_init - initialize TCP socket * @v9ses: session information @@ -154,9 +174,9 @@ v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data) int rc = 0; struct v9fs_trans_sock *ts = NULL; struct v9fs_transport *trans = v9ses->transport; + int fd; - sema_init(&trans->writelock, 1); - sema_init(&trans->readlock, 1); + trans->status = Disconnected; ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL); @@ -165,6 +185,7 @@ v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data) trans->priv = ts; ts->s = NULL; + ts->filp = NULL; if (!addr) return -EINVAL; @@ -185,7 +206,18 @@ v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data) return rc; } csocket->sk->sk_allocation = GFP_NOIO; + + fd = sock_map_fd(csocket); + if (fd < 0) { + sock_release(csocket); + kfree(ts); + trans->priv = NULL; + return fd; + } + ts->s = csocket; + ts->filp = fget(fd); + ts->filp->f_flags |= O_NONBLOCK; trans->status = Connected; return 0; @@ -203,7 +235,7 @@ static int v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name, char *data) { - int rc; + int rc, fd; struct socket *csocket; struct sockaddr_un sun_server; struct v9fs_transport *trans; @@ -213,6 +245,8 @@ v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name, csocket = NULL; trans = v9ses->transport; + trans->status = Disconnected; + if (strlen(dev_name) > UNIX_PATH_MAX) { eprintk(KERN_ERR, "v9fs_trans_unix: address too long: %s\n", dev_name); @@ -225,9 +259,7 @@ v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name, trans->priv = ts; ts->s = NULL; - - sema_init(&trans->writelock, 1); - sema_init(&trans->readlock, 1); + ts->filp = NULL; sun_server.sun_family = PF_UNIX; strcpy(sun_server.sun_path, dev_name); @@ -241,7 +273,18 @@ v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name, return rc; } csocket->sk->sk_allocation = GFP_NOIO; + + fd = sock_map_fd(csocket); + if (fd < 0) { + sock_release(csocket); + kfree(ts); + trans->priv = NULL; + return fd; + } + ts->s = csocket; + ts->filp = fget(fd); + ts->filp->f_flags |= O_NONBLOCK; trans->status = Connected; return 0; @@ -262,12 +305,11 @@ static void v9fs_sock_close(struct v9fs_transport *trans) ts = trans->priv; - if ((ts) && (ts->s)) { - dprintk(DEBUG_TRANS, "closing the socket %p\n", ts->s); - sock_release(ts->s); + if ((ts) && (ts->filp)) { + fput(ts->filp); + ts->filp = NULL; ts->s = NULL; trans->status = Disconnected; - dprintk(DEBUG_TRANS, "socket closed\n"); } kfree(ts); @@ -280,6 +322,7 @@ struct v9fs_transport v9fs_trans_tcp = { .write = v9fs_sock_send, .read = v9fs_sock_recv, .close = v9fs_sock_close, + .poll = v9fs_sock_poll, }; struct v9fs_transport v9fs_trans_unix = { @@ -287,4 +330,5 @@ struct v9fs_transport v9fs_trans_unix = { .write = v9fs_sock_send, .read = v9fs_sock_recv, .close = v9fs_sock_close, + .poll = v9fs_sock_poll, }; diff --git a/fs/9p/transport.h b/fs/9p/transport.h index 9e9cd41..91fcdb9 100644 --- a/fs/9p/transport.h +++ b/fs/9p/transport.h @@ -3,6 +3,7 @@ * * Transport Definition * + * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> * * This program is free software; you can redistribute it and/or modify @@ -31,14 +32,13 @@ enum v9fs_transport_status { struct v9fs_transport { enum v9fs_transport_status status; - struct semaphore writelock; - struct semaphore readlock; void *priv; int (*init) (struct v9fs_session_info *, const char *, char *); int (*write) (struct v9fs_transport *, void *, int); int (*read) (struct v9fs_transport *, void *, int); void (*close) (struct v9fs_transport *); + unsigned int (*poll)(struct v9fs_transport *, struct poll_table_struct *); }; extern struct v9fs_transport v9fs_trans_tcp; diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 418c374..5250c42 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -37,7 +37,6 @@ #include "v9fs_vfs.h" #include "transport.h" #include "mux.h" -#include "conv.h" /* TODO: sysfs or debugfs interface */ int v9fs_debug_level = 0; /* feature-rific global debug level */ @@ -213,7 +212,8 @@ retry: return -1; } - error = idr_get_new(&p->pool, NULL, &i); + /* no need to store exactly p, we just need something non-null */ + error = idr_get_new(&p->pool, p, &i); up(&p->lock); if (error == -EAGAIN) @@ -243,6 +243,16 @@ void v9fs_put_idpool(int id, struct v9fs_idpool *p) } /** + * v9fs_check_idpool - check if the specified id is available + * @id - id to check + * @p - pool + */ +int v9fs_check_idpool(int id, struct v9fs_idpool *p) +{ + return idr_find(&p->pool, id) != NULL; +} + +/** * v9fs_session_init - initialize session * @v9ses: session information structure * @dev_name: device being mounted @@ -259,6 +269,7 @@ v9fs_session_init(struct v9fs_session_info *v9ses, int n = 0; int newfid = -1; int retval = -EINVAL; + struct v9fs_str *version; v9ses->name = __getname(); if (!v9ses->name) @@ -281,9 +292,6 @@ v9fs_session_init(struct v9fs_session_info *v9ses, /* id pools that are session-dependent: FIDs and TIDs */ idr_init(&v9ses->fidpool.pool); init_MUTEX(&v9ses->fidpool.lock); - idr_init(&v9ses->tidpool.pool); - init_MUTEX(&v9ses->tidpool.lock); - switch (v9ses->proto) { case PROTO_TCP: @@ -320,7 +328,12 @@ v9fs_session_init(struct v9fs_session_info *v9ses, v9ses->shutdown = 0; v9ses->session_hung = 0; - if ((retval = v9fs_mux_init(v9ses, dev_name)) < 0) { + v9ses->mux = v9fs_mux_init(v9ses->transport, v9ses->maxdata + V9FS_IOHDRSZ, + &v9ses->extended); + + if (IS_ERR(v9ses->mux)) { + retval = PTR_ERR(v9ses->mux); + v9ses->mux = NULL; dprintk(DEBUG_ERROR, "problem initializing mux\n"); goto SessCleanUp; } @@ -339,13 +352,16 @@ v9fs_session_init(struct v9fs_session_info *v9ses, goto FreeFcall; } - /* Really should check for 9P1 and report error */ - if (!strcmp(fcall->params.rversion.version, "9P2000.u")) { + version = &fcall->params.rversion.version; + if (version->len==8 && !memcmp(version->str, "9P2000.u", 8)) { dprintk(DEBUG_9P, "9P2000 UNIX extensions enabled\n"); v9ses->extended = 1; - } else { + } else if (version->len==6 && !memcmp(version->str, "9P2000", 6)) { dprintk(DEBUG_9P, "9P2000 legacy mode enabled\n"); v9ses->extended = 0; + } else { + retval = -EREMOTEIO; + goto FreeFcall; } n = fcall->params.rversion.msize; @@ -381,7 +397,7 @@ v9fs_session_init(struct v9fs_session_info *v9ses, } if (v9ses->afid != ~0) { - if (v9fs_t_clunk(v9ses, v9ses->afid, NULL)) + if (v9fs_t_clunk(v9ses, v9ses->afid)) dprintk(DEBUG_ERROR, "clunk failed\n"); } @@ -403,13 +419,16 @@ v9fs_session_init(struct v9fs_session_info *v9ses, void v9fs_session_close(struct v9fs_session_info *v9ses) { - if (v9ses->recvproc) { - send_sig(SIGKILL, v9ses->recvproc, 1); - wait_for_completion(&v9ses->proccmpl); + if (v9ses->mux) { + v9fs_mux_destroy(v9ses->mux); + v9ses->mux = NULL; } - if (v9ses->transport) + if (v9ses->transport) { v9ses->transport->close(v9ses->transport); + kfree(v9ses->transport); + v9ses->transport = NULL; + } __putname(v9ses->name); __putname(v9ses->remotename); @@ -420,8 +439,9 @@ void v9fs_session_close(struct v9fs_session_info *v9ses) * and cancel all pending requests. */ void v9fs_session_cancel(struct v9fs_session_info *v9ses) { + dprintk(DEBUG_ERROR, "cancel session %p\n", v9ses); v9ses->transport->status = Disconnected; - v9fs_mux_cancel_requests(v9ses, -EIO); + v9fs_mux_cancel(v9ses->mux, -EIO); } extern int v9fs_error_init(void); @@ -433,11 +453,17 @@ extern int v9fs_error_init(void); static int __init init_v9fs(void) { + int ret; + v9fs_error_init(); printk(KERN_INFO "Installing v9fs 9P2000 file system support\n"); - return register_filesystem(&v9fs_fs_type); + ret = v9fs_mux_global_init(); + if (!ret) + ret = register_filesystem(&v9fs_fs_type); + + return ret; } /** @@ -447,6 +473,7 @@ static int __init init_v9fs(void) static void __exit exit_v9fs(void) { + v9fs_mux_global_exit(); unregister_filesystem(&v9fs_fs_type); } diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index 45dcef4..f337da7 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -57,24 +57,14 @@ struct v9fs_session_info { /* book keeping */ struct v9fs_idpool fidpool; /* The FID pool for file descriptors */ - struct v9fs_idpool tidpool; /* The TID pool for transactions ids */ - /* transport information */ struct v9fs_transport *transport; + struct v9fs_mux_data *mux; int inprogress; /* session in progress => true */ int shutdown; /* session shutting down. no more attaches. */ unsigned char session_hung; - - /* mux private data */ - struct v9fs_fcall *curfcall; - wait_queue_head_t read_wait; - struct completion fcread; - struct completion proccmpl; - struct task_struct *recvproc; - - spinlock_t muxlock; - struct list_head mux_fcalls; + struct dentry *debugfs_dir; }; /* possible values of ->proto */ @@ -84,11 +74,14 @@ enum { PROTO_FD, }; +extern struct dentry *v9fs_debugfs_root; + int v9fs_session_init(struct v9fs_session_info *, const char *, char *); struct v9fs_session_info *v9fs_inode2v9ses(struct inode *); void v9fs_session_close(struct v9fs_session_info *v9ses); int v9fs_get_idpool(struct v9fs_idpool *p); void v9fs_put_idpool(int id, struct v9fs_idpool *p); +int v9fs_check_idpool(int id, struct v9fs_idpool *p); void v9fs_session_cancel(struct v9fs_session_info *v9ses); #define V9FS_MAGIC 0x01021997 diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index 2f2cea7..c78502a 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -45,9 +45,8 @@ extern struct dentry_operations v9fs_dentry_operations; struct inode *v9fs_get_inode(struct super_block *sb, int mode); ino_t v9fs_qid2ino(struct v9fs_qid *qid); -void v9fs_mistat2inode(struct v9fs_stat *, struct inode *, - struct super_block *); +void v9fs_stat2inode(struct v9fs_stat *, struct inode *, struct super_block *); int v9fs_dir_release(struct inode *inode, struct file *filp); int v9fs_file_open(struct inode *inode, struct file *file); -void v9fs_inode2mistat(struct inode *inode, struct v9fs_stat *mistat); +void v9fs_inode2stat(struct inode *inode, struct v9fs_stat *stat); void v9fs_dentry_release(struct dentry *); diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index a6aa947..2dd806d 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -40,7 +40,6 @@ #include "v9fs.h" #include "9p.h" #include "v9fs_vfs.h" -#include "conv.h" #include "fid.h" /** @@ -95,24 +94,22 @@ static int v9fs_dentry_validate(struct dentry *dentry, struct nameidata *nd) void v9fs_dentry_release(struct dentry *dentry) { + int err; + dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); if (dentry->d_fsdata != NULL) { struct list_head *fid_list = dentry->d_fsdata; struct v9fs_fid *temp = NULL; struct v9fs_fid *current_fid = NULL; - struct v9fs_fcall *fcall = NULL; list_for_each_entry_safe(current_fid, temp, fid_list, list) { - if (v9fs_t_clunk - (current_fid->v9ses, current_fid->fid, &fcall)) - dprintk(DEBUG_ERROR, "clunk failed: %s\n", - FCALL_ERROR(fcall)); + err = v9fs_t_clunk(current_fid->v9ses, current_fid->fid); - v9fs_put_idpool(current_fid->fid, - ¤t_fid->v9ses->fidpool); + if (err < 0) + dprintk(DEBUG_ERROR, "clunk failed: %d name %s\n", + err, dentry->d_iname); - kfree(fcall); v9fs_fid_destroy(current_fid); } diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 57a43b8..ae6d032 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -37,8 +37,8 @@ #include "debug.h" #include "v9fs.h" #include "9p.h" -#include "v9fs_vfs.h" #include "conv.h" +#include "v9fs_vfs.h" #include "fid.h" /** @@ -74,20 +74,16 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) struct inode *inode = filp->f_dentry->d_inode; struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); struct v9fs_fid *file = filp->private_data; - unsigned int i, n; + unsigned int i, n, s; int fid = -1; int ret = 0; - struct v9fs_stat *mi = NULL; + struct v9fs_stat stat; int over = 0; dprintk(DEBUG_VFS, "name %s\n", filp->f_dentry->d_name.name); fid = file->fid; - mi = kmalloc(v9ses->maxdata, GFP_KERNEL); - if (!mi) - return -ENOMEM; - if (file->rdir_fcall && (filp->f_pos != file->rdir_pos)) { kfree(file->rdir_fcall); file->rdir_fcall = NULL; @@ -97,20 +93,20 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) n = file->rdir_fcall->params.rread.count; i = file->rdir_fpos; while (i < n) { - int s = v9fs_deserialize_stat(v9ses, - file->rdir_fcall->params.rread.data + i, - n - i, mi, v9ses->maxdata); + s = v9fs_deserialize_stat( + file->rdir_fcall->params.rread.data + i, + n - i, &stat, v9ses->extended); if (s == 0) { dprintk(DEBUG_ERROR, - "error while deserializing mistat\n"); + "error while deserializing stat\n"); ret = -EIO; goto FreeStructs; } - over = filldir(dirent, mi->name, strlen(mi->name), - filp->f_pos, v9fs_qid2ino(&mi->qid), - dt_type(mi)); + over = filldir(dirent, stat.name.str, stat.name.len, + filp->f_pos, v9fs_qid2ino(&stat.qid), + dt_type(&stat)); if (over) { file->rdir_fpos = i; @@ -130,7 +126,7 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) while (!over) { ret = v9fs_t_read(v9ses, fid, filp->f_pos, - v9ses->maxdata-V9FS_IOHDRSZ, &fcall); + v9ses->maxdata-V9FS_IOHDRSZ, &fcall); if (ret < 0) { dprintk(DEBUG_ERROR, "error while reading: %d: %p\n", ret, fcall); @@ -141,19 +137,18 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) n = ret; i = 0; while (i < n) { - int s = v9fs_deserialize_stat(v9ses, - fcall->params.rread.data + i, n - i, mi, - v9ses->maxdata); + s = v9fs_deserialize_stat(fcall->params.rread.data + i, + n - i, &stat, v9ses->extended); if (s == 0) { dprintk(DEBUG_ERROR, - "error while deserializing mistat\n"); + "error while deserializing stat\n"); return -EIO; } - over = filldir(dirent, mi->name, strlen(mi->name), - filp->f_pos, v9fs_qid2ino(&mi->qid), - dt_type(mi)); + over = filldir(dirent, stat.name.str, stat.name.len, + filp->f_pos, v9fs_qid2ino(&stat.qid), + dt_type(&stat)); if (over) { file->rdir_fcall = fcall; @@ -172,7 +167,6 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) FreeStructs: kfree(fcall); - kfree(mi); return ret; } @@ -193,18 +187,15 @@ int v9fs_dir_release(struct inode *inode, struct file *filp) fid->fid); fidnum = fid->fid; - filemap_fdatawrite(inode->i_mapping); - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); if (fidnum >= 0) { dprintk(DEBUG_VFS, "fidopen: %d v9f->fid: %d\n", fid->fidopen, fid->fid); - if (v9fs_t_clunk(v9ses, fidnum, NULL)) + if (v9fs_t_clunk(v9ses, fidnum)) dprintk(DEBUG_ERROR, "clunk failed\n"); - v9fs_put_idpool(fid->fid, &v9ses->fidpool); - kfree(fid->rdir_fcall); kfree(fid); diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 89c849d..6852f0e 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -32,6 +32,7 @@ #include <linux/string.h> #include <linux/smp_lock.h> #include <linux/inet.h> +#include <linux/version.h> #include <linux/list.h> #include <asm/uaccess.h> #include <linux/idr.h> @@ -117,9 +118,7 @@ int v9fs_file_open(struct inode *inode, struct file *file) result = v9fs_t_open(v9ses, newfid, open_mode, &fcall); if (result < 0) { - dprintk(DEBUG_ERROR, - "open failed, open_mode 0x%x: %s\n", open_mode, - FCALL_ERROR(fcall)); + PRINT_FCALL_ERROR("open failed", fcall); kfree(fcall); return result; } @@ -165,8 +164,7 @@ static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl) return -ENOLCK; if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { - filemap_fdatawrite(inode->i_mapping); - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); invalidate_inode_pages(&inode->i_data); } @@ -257,7 +255,6 @@ v9fs_file_write(struct file *filp, const char __user * data, int result = -EIO; int rsize = 0; int total = 0; - char *buf; dprintk(DEBUG_VFS, "data %p count %d offset %x\n", data, (int)count, (int)*offset); @@ -265,28 +262,14 @@ v9fs_file_write(struct file *filp, const char __user * data, if (v9fid->iounit != 0 && rsize > v9fid->iounit) rsize = v9fid->iounit; - buf = kmalloc(v9ses->maxdata - V9FS_IOHDRSZ, GFP_KERNEL); - if (!buf) - return -ENOMEM; - do { if (count < rsize) rsize = count; - result = copy_from_user(buf, data, rsize); - if (result) { - dprintk(DEBUG_ERROR, "Problem copying from user\n"); - kfree(buf); - return -EFAULT; - } - - dump_data(buf, rsize); - result = v9fs_t_write(v9ses, fid, *offset, rsize, buf, &fcall); + result = v9fs_t_write(v9ses, fid, *offset, rsize, data, &fcall); if (result < 0) { - eprintk(KERN_ERR, "error while writing: %s(%d)\n", - FCALL_ERROR(fcall), result); + PRINT_FCALL_ERROR("error while writing", fcall); kfree(fcall); - kfree(buf); return result; } else *offset += result; @@ -306,7 +289,6 @@ v9fs_file_write(struct file *filp, const char __user * data, total += result; } while (count); - kfree(buf); return total; } diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 0ea965c..d933ef1 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -40,7 +40,6 @@ #include "v9fs.h" #include "9p.h" #include "v9fs_vfs.h" -#include "conv.h" #include "fid.h" static struct inode_operations v9fs_dir_inode_operations; @@ -127,100 +126,32 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode) } /** - * v9fs_blank_mistat - helper function to setup a 9P stat structure + * v9fs_blank_wstat - helper function to setup a 9P stat structure * @v9ses: 9P session info (for determining extended mode) - * @mistat: structure to initialize + * @wstat: structure to initialize * */ static void -v9fs_blank_mistat(struct v9fs_session_info *v9ses, struct v9fs_stat *mistat) +v9fs_blank_wstat(struct v9fs_wstat *wstat) { - mistat->type = ~0; - mistat->dev = ~0; - mistat->qid.type = ~0; - mistat->qid.version = ~0; - *((long long *)&mistat->qid.path) = ~0; - mistat->mode = ~0; - mistat->atime = ~0; - mistat->mtime = ~0; - mistat->length = ~0; - mistat->name = mistat->data; - mistat->uid = mistat->data; - mistat->gid = mistat->data; - mistat->muid = mistat->data; - if (v9ses->extended) { - mistat->n_uid = ~0; - mistat->n_gid = ~0; - mistat->n_muid = ~0; - mistat->extension = mistat->data; - } - *mistat->data = 0; -} - -/** - * v9fs_mistat2unix - convert mistat to unix stat - * @mistat: Plan 9 metadata (mistat) structure - * @buf: unix metadata (stat) structure to populate - * @sb: superblock - * - */ - -static void -v9fs_mistat2unix(struct v9fs_stat *mistat, struct stat *buf, - struct super_block *sb) -{ - struct v9fs_session_info *v9ses = sb ? sb->s_fs_info : NULL; - - buf->st_nlink = 1; - - buf->st_atime = mistat->atime; - buf->st_mtime = mistat->mtime; - buf->st_ctime = mistat->mtime; - - buf->st_uid = (unsigned short)-1; - buf->st_gid = (unsigned short)-1; - - if (v9ses && v9ses->extended) { - /* TODO: string to uid mapping via user-space daemon */ - if (mistat->n_uid != -1) - sscanf(mistat->uid, "%x", (unsigned int *)&buf->st_uid); - - if (mistat->n_gid != -1) - sscanf(mistat->gid, "%x", (unsigned int *)&buf->st_gid); - } - - if (buf->st_uid == (unsigned short)-1) - buf->st_uid = v9ses->uid; - if (buf->st_gid == (unsigned short)-1) - buf->st_gid = v9ses->gid; - - buf->st_mode = p9mode2unixmode(v9ses, mistat->mode); - if ((S_ISBLK(buf->st_mode)) || (S_ISCHR(buf->st_mode))) { - char type = 0; - int major = -1; - int minor = -1; - sscanf(mistat->extension, "%c %u %u", &type, &major, &minor); - switch (type) { - case 'c': - buf->st_mode &= ~S_IFBLK; - buf->st_mode |= S_IFCHR; - break; - case 'b': - break; - default: - dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n", - type, mistat->extension); - }; - buf->st_rdev = MKDEV(major, minor); - } else - buf->st_rdev = 0; - - buf->st_size = mistat->length; - - buf->st_blksize = sb->s_blocksize; - buf->st_blocks = - (buf->st_size + buf->st_blksize - 1) >> sb->s_blocksize_bits; + wstat->type = ~0; + wstat->dev = ~0; + wstat->qid.type = ~0; + wstat->qid.version = ~0; + *((long long *)&wstat->qid.path) = ~0; + wstat->mode = ~0; + wstat->atime = ~0; + wstat->mtime = ~0; + wstat->length = ~0; + wstat->name = NULL; + wstat->uid = NULL; + wstat->gid = NULL; + wstat->muid = NULL; + wstat->n_uid = ~0; + wstat->n_gid = ~0; + wstat->n_muid = ~0; + wstat->extension = NULL; } /** @@ -312,12 +243,12 @@ v9fs_create(struct inode *dir, struct inode *file_inode = NULL; struct v9fs_fcall *fcall = NULL; struct v9fs_qid qid; - struct stat newstat; int dirfidnum = -1; long newfid = -1; int result = 0; unsigned int iounit = 0; int wfidno = -1; + int err; perm = unixmode2p9mode(v9ses, perm); @@ -349,57 +280,64 @@ v9fs_create(struct inode *dir, result = v9fs_t_walk(v9ses, dirfidnum, newfid, NULL, &fcall); if (result < 0) { - dprintk(DEBUG_ERROR, "clone error: %s\n", FCALL_ERROR(fcall)); + PRINT_FCALL_ERROR("clone error", fcall); v9fs_put_idpool(newfid, &v9ses->fidpool); newfid = -1; goto CleanUpFid; } kfree(fcall); + fcall = NULL; result = v9fs_t_create(v9ses, newfid, (char *)file_dentry->d_name.name, perm, open_mode, &fcall); if (result < 0) { - dprintk(DEBUG_ERROR, "create fails: %s(%d)\n", - FCALL_ERROR(fcall), result); - + PRINT_FCALL_ERROR("create fails", fcall); goto CleanUpFid; } iounit = fcall->params.rcreate.iounit; qid = fcall->params.rcreate.qid; kfree(fcall); + fcall = NULL; - fid = v9fs_fid_create(file_dentry, v9ses, newfid, 1); - dprintk(DEBUG_VFS, "fid %p %d\n", fid, fid->fidcreate); - if (!fid) { - result = -ENOMEM; - goto CleanUpFid; - } + if (!(perm&V9FS_DMDIR)) { + fid = v9fs_fid_create(file_dentry, v9ses, newfid, 1); + dprintk(DEBUG_VFS, "fid %p %d\n", fid, fid->fidcreate); + if (!fid) { + result = -ENOMEM; + goto CleanUpFid; + } - fid->qid = qid; - fid->iounit = iounit; + fid->qid = qid; + fid->iounit = iounit; + } else { + err = v9fs_t_clunk(v9ses, newfid); + newfid = -1; + if (err < 0) + dprintk(DEBUG_ERROR, "clunk for mkdir failed: %d\n", err); + } /* walk to the newly created file and put the fid in the dentry */ wfidno = v9fs_get_idpool(&v9ses->fidpool); - if (newfid < 0) { + if (wfidno < 0) { eprintk(KERN_WARNING, "no free fids available\n"); return -ENOSPC; } result = v9fs_t_walk(v9ses, dirfidnum, wfidno, - (char *) file_dentry->d_name.name, NULL); + (char *) file_dentry->d_name.name, &fcall); if (result < 0) { - dprintk(DEBUG_ERROR, "clone error: %s\n", FCALL_ERROR(fcall)); + PRINT_FCALL_ERROR("clone error", fcall); v9fs_put_idpool(wfidno, &v9ses->fidpool); wfidno = -1; goto CleanUpFid; } + kfree(fcall); + fcall = NULL; if (!v9fs_fid_create(file_dentry, v9ses, wfidno, 0)) { - if (!v9fs_t_clunk(v9ses, newfid, &fcall)) { - v9fs_put_idpool(wfidno, &v9ses->fidpool); - } + v9fs_put_idpool(wfidno, &v9ses->fidpool); goto CleanUpFid; } @@ -409,62 +347,43 @@ v9fs_create(struct inode *dir, (perm & V9FS_DMDEVICE)) return 0; - result = v9fs_t_stat(v9ses, newfid, &fcall); + result = v9fs_t_stat(v9ses, wfidno, &fcall); if (result < 0) { - dprintk(DEBUG_ERROR, "stat error: %s(%d)\n", FCALL_ERROR(fcall), - result); + PRINT_FCALL_ERROR("stat error", fcall); goto CleanUpFid; } - v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb); - file_inode = v9fs_get_inode(sb, newstat.st_mode); + file_inode = v9fs_get_inode(sb, + p9mode2unixmode(v9ses, fcall->params.rstat.stat.mode)); + if ((!file_inode) || IS_ERR(file_inode)) { dprintk(DEBUG_ERROR, "create inode failed\n"); result = -EBADF; goto CleanUpFid; } - v9fs_mistat2inode(fcall->params.rstat.stat, file_inode, sb); + v9fs_stat2inode(&fcall->params.rstat.stat, file_inode, sb); kfree(fcall); fcall = NULL; file_dentry->d_op = &v9fs_dentry_operations; d_instantiate(file_dentry, file_inode); - if (perm & V9FS_DMDIR) { - if (!v9fs_t_clunk(v9ses, newfid, &fcall)) - v9fs_put_idpool(newfid, &v9ses->fidpool); - else - dprintk(DEBUG_ERROR, "clunk for mkdir failed: %s\n", - FCALL_ERROR(fcall)); - kfree(fcall); - fid->fidopen = 0; - fid->fidcreate = 0; - d_drop(file_dentry); - } - return 0; CleanUpFid: kfree(fcall); + fcall = NULL; if (newfid >= 0) { - if (!v9fs_t_clunk(v9ses, newfid, &fcall)) - v9fs_put_idpool(newfid, &v9ses->fidpool); - else - dprintk(DEBUG_ERROR, "clunk failed: %s\n", - FCALL_ERROR(fcall)); - - kfree(fcall); + err = v9fs_t_clunk(v9ses, newfid); + if (err < 0) + dprintk(DEBUG_ERROR, "clunk failed: %d\n", err); } if (wfidno >= 0) { - if (!v9fs_t_clunk(v9ses, wfidno, &fcall)) - v9fs_put_idpool(wfidno, &v9ses->fidpool); - else - dprintk(DEBUG_ERROR, "clunk failed: %s\n", - FCALL_ERROR(fcall)); - - kfree(fcall); + err = v9fs_t_clunk(v9ses, wfidno); + if (err < 0) + dprintk(DEBUG_ERROR, "clunk failed: %d\n", err); } return result; } @@ -509,10 +428,9 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) } result = v9fs_t_remove(v9ses, fid, &fcall); - if (result < 0) - dprintk(DEBUG_ERROR, "remove of file fails: %s(%d)\n", - FCALL_ERROR(fcall), result); - else { + if (result < 0) { + PRINT_FCALL_ERROR("remove fails", fcall); + } else { v9fs_put_idpool(fid, &v9ses->fidpool); v9fs_fid_destroy(v9fid); } @@ -567,7 +485,6 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, struct v9fs_fid *fid; struct inode *inode; struct v9fs_fcall *fcall = NULL; - struct stat newstat; int dirfidnum = -1; int newfid = -1; int result = 0; @@ -620,8 +537,8 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, goto FreeFcall; } - v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb); - inode = v9fs_get_inode(sb, newstat.st_mode); + inode = v9fs_get_inode(sb, p9mode2unixmode(v9ses, + fcall->params.rstat.stat.mode)); if (IS_ERR(inode) && (PTR_ERR(inode) == -ENOSPC)) { eprintk(KERN_WARNING, "inode alloc failes, returns %ld\n", @@ -631,7 +548,7 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, goto FreeFcall; } - inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat->qid); + inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat.qid); fid = v9fs_fid_create(dentry, v9ses, newfid, 0); if (fid == NULL) { @@ -640,10 +557,10 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, goto FreeFcall; } - fid->qid = fcall->params.rstat.stat->qid; + fid->qid = fcall->params.rstat.stat.qid; dentry->d_op = &v9fs_dentry_operations; - v9fs_mistat2inode(fcall->params.rstat.stat, inode, inode->i_sb); + v9fs_stat2inode(&fcall->params.rstat.stat, inode, inode->i_sb); d_add(dentry, inode); kfree(fcall); @@ -699,7 +616,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, v9fs_fid_lookup(old_dentry->d_parent); struct v9fs_fid *newdirfid = v9fs_fid_lookup(new_dentry->d_parent); - struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); + struct v9fs_wstat wstat; struct v9fs_fcall *fcall = NULL; int fid = -1; int olddirfidnum = -1; @@ -708,9 +625,6 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, dprintk(DEBUG_VFS, "\n"); - if (!mistat) - return -ENOMEM; - if ((!oldfid) || (!olddirfid) || (!newdirfid)) { dprintk(DEBUG_ERROR, "problem with arguments\n"); return -EBADF; @@ -734,26 +648,15 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto FreeFcallnBail; } - v9fs_blank_mistat(v9ses, mistat); + v9fs_blank_wstat(&wstat); + wstat.muid = v9ses->name; + wstat.name = (char *) new_dentry->d_name.name; - strcpy(mistat->data + 1, v9ses->name); - mistat->name = mistat->data + 1 + strlen(v9ses->name); - - if (new_dentry->d_name.len > - (v9ses->maxdata - strlen(v9ses->name) - sizeof(struct v9fs_stat))) { - dprintk(DEBUG_ERROR, "new name too long\n"); - goto FreeFcallnBail; - } - - strcpy(mistat->name, new_dentry->d_name.name); - retval = v9fs_t_wstat(v9ses, fid, mistat, &fcall); + retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall); FreeFcallnBail: - kfree(mistat); - if (retval < 0) - dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", - FCALL_ERROR(fcall)); + PRINT_FCALL_ERROR("wstat error", fcall); kfree(fcall); return retval; @@ -788,7 +691,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, if (err < 0) dprintk(DEBUG_ERROR, "stat error\n"); else { - v9fs_mistat2inode(fcall->params.rstat.stat, dentry->d_inode, + v9fs_stat2inode(&fcall->params.rstat.stat, dentry->d_inode, dentry->d_inode->i_sb); generic_fillattr(dentry->d_inode, stat); } @@ -809,57 +712,44 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); struct v9fs_fid *fid = v9fs_fid_lookup(dentry); struct v9fs_fcall *fcall = NULL; - struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); + struct v9fs_wstat wstat; int res = -EPERM; dprintk(DEBUG_VFS, "\n"); - if (!mistat) - return -ENOMEM; - if (!fid) { dprintk(DEBUG_ERROR, "Couldn't find fid associated with dentry\n"); return -EBADF; } - v9fs_blank_mistat(v9ses, mistat); + v9fs_blank_wstat(&wstat); if (iattr->ia_valid & ATTR_MODE) - mistat->mode = unixmode2p9mode(v9ses, iattr->ia_mode); + wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode); if (iattr->ia_valid & ATTR_MTIME) - mistat->mtime = iattr->ia_mtime.tv_sec; + wstat.mtime = iattr->ia_mtime.tv_sec; if (iattr->ia_valid & ATTR_ATIME) - mistat->atime = iattr->ia_atime.tv_sec; + wstat.atime = iattr->ia_atime.tv_sec; if (iattr->ia_valid & ATTR_SIZE) - mistat->length = iattr->ia_size; + wstat.length = iattr->ia_size; if (v9ses->extended) { - char *ptr = mistat->data+1; - - if (iattr->ia_valid & ATTR_UID) { - mistat->uid = ptr; - ptr += 1+sprintf(ptr, "%08x", iattr->ia_uid); - mistat->n_uid = iattr->ia_uid; - } + if (iattr->ia_valid & ATTR_UID) + wstat.n_uid = iattr->ia_uid; - if (iattr->ia_valid & ATTR_GID) { - mistat->gid = ptr; - ptr += 1+sprintf(ptr, "%08x", iattr->ia_gid); - mistat->n_gid = iattr->ia_gid; - } + if (iattr->ia_valid & ATTR_GID) + wstat.n_gid = iattr->ia_gid; } - res = v9fs_t_wstat(v9ses, fid->fid, mistat, &fcall); + res = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall); if (res < 0) - dprintk(DEBUG_ERROR, "wstat error: %s\n", FCALL_ERROR(fcall)); + PRINT_FCALL_ERROR("wstat error", fcall); - kfree(mistat); kfree(fcall); - if (res >= 0) res = inode_setattr(dentry->d_inode, iattr); @@ -867,51 +757,47 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) } /** - * v9fs_mistat2inode - populate an inode structure with mistat info - * @mistat: Plan 9 metadata (mistat) structure + * v9fs_stat2inode - populate an inode structure with mistat info + * @stat: Plan 9 metadata (mistat) structure * @inode: inode to populate * @sb: superblock of filesystem * */ void -v9fs_mistat2inode(struct v9fs_stat *mistat, struct inode *inode, - struct super_block *sb) +v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode, + struct super_block *sb) { + int n; + char ext[32]; struct v9fs_session_info *v9ses = sb->s_fs_info; inode->i_nlink = 1; - inode->i_atime.tv_sec = mistat->atime; - inode->i_mtime.tv_sec = mistat->mtime; - inode->i_ctime.tv_sec = mistat->mtime; + inode->i_atime.tv_sec = stat->atime; + inode->i_mtime.tv_sec = stat->mtime; + inode->i_ctime.tv_sec = stat->mtime; - inode->i_uid = -1; - inode->i_gid = -1; + inode->i_uid = v9ses->uid; + inode->i_gid = v9ses->gid; if (v9ses->extended) { - /* TODO: string to uid mapping via user-space daemon */ - inode->i_uid = mistat->n_uid; - inode->i_gid = mistat->n_gid; - - if (mistat->n_uid == -1) - sscanf(mistat->uid, "%x", &inode->i_uid); - - if (mistat->n_gid == -1) - sscanf(mistat->gid, "%x", &inode->i_gid); + inode->i_uid = stat->n_uid; + inode->i_gid = stat->n_gid; } - if (inode->i_uid == -1) - inode->i_uid = v9ses->uid; - if (inode->i_gid == -1) - inode->i_gid = v9ses->gid; - - inode->i_mode = p9mode2unixmode(v9ses, mistat->mode); + 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; - sscanf(mistat->extension, "%c %u %u", &type, &major, &minor); + + n = stat->extension.len; + if (n > sizeof(ext)-1) + n = sizeof(ext)-1; + memmove(ext, stat->extension.str, n); + ext[n] = 0; + sscanf(ext, "%c %u %u", &type, &major, &minor); switch (type) { case 'c': inode->i_mode &= ~S_IFBLK; @@ -920,14 +806,14 @@ v9fs_mistat2inode(struct v9fs_stat *mistat, struct inode *inode, case 'b': break; default: - dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n", - type, mistat->extension); + dprintk(DEBUG_ERROR, "Unknown special type %c (%.*s)\n", + type, stat->extension.len, stat->extension.str); }; inode->i_rdev = MKDEV(major, minor); } else inode->i_rdev = 0; - inode->i_size = mistat->length; + inode->i_size = stat->length; inode->i_blksize = sb->s_blocksize; inode->i_blocks = @@ -955,71 +841,6 @@ ino_t v9fs_qid2ino(struct v9fs_qid *qid) } /** - * v9fs_vfs_symlink - helper function to create symlinks - * @dir: directory inode containing symlink - * @dentry: dentry for symlink - * @symname: symlink data - * - * See 9P2000.u RFC for more information - * - */ - -static int -v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -{ - int retval = -EPERM; - struct v9fs_fid *newfid; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); - struct v9fs_fcall *fcall = NULL; - struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); - - dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, - symname); - - if (!mistat) - return -ENOMEM; - - if (!v9ses->extended) { - dprintk(DEBUG_ERROR, "not extended\n"); - goto FreeFcall; - } - - /* issue a create */ - retval = v9fs_create(dir, dentry, S_IFLNK, 0); - if (retval != 0) - goto FreeFcall; - - newfid = v9fs_fid_lookup(dentry); - - /* issue a twstat */ - v9fs_blank_mistat(v9ses, mistat); - strcpy(mistat->data + 1, symname); - mistat->extension = mistat->data + 1; - retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); - if (retval < 0) { - dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", - FCALL_ERROR(fcall)); - goto FreeFcall; - } - - kfree(fcall); - - if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { - dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", - FCALL_ERROR(fcall)); - goto FreeFcall; - } - - d_drop(dentry); /* FID - will this also clunk? */ - - FreeFcall: - kfree(mistat); - kfree(fcall); - - return retval; -} - -/** * v9fs_readlink - read a symlink's location (internal version) * @dentry: dentry for symlink * @buffer: buffer to load symlink location into @@ -1058,16 +879,17 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) if (!fcall) return -EIO; - if (!(fcall->params.rstat.stat->mode & V9FS_DMSYMLINK)) { + if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) { retval = -EINVAL; goto FreeFcall; } /* copy extension buffer into buffer */ - if (strlen(fcall->params.rstat.stat->extension) < buflen) - buflen = strlen(fcall->params.rstat.stat->extension); + if (fcall->params.rstat.stat.extension.len < buflen) + buflen = fcall->params.rstat.stat.extension.len; - memcpy(buffer, fcall->params.rstat.stat->extension, buflen + 1); + memcpy(buffer, fcall->params.rstat.stat.extension.str, buflen - 1); + buffer[buflen-1] = 0; retval = buflen; @@ -1157,6 +979,77 @@ static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void __putname(s); } +static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, + int mode, const char *extension) +{ + int err, retval; + struct v9fs_session_info *v9ses; + struct v9fs_fcall *fcall; + struct v9fs_fid *fid; + struct v9fs_wstat wstat; + + v9ses = v9fs_inode2v9ses(dir); + retval = -EPERM; + fcall = NULL; + + if (!v9ses->extended) { + dprintk(DEBUG_ERROR, "not extended\n"); + goto free_mem; + } + + /* issue a create */ + retval = v9fs_create(dir, dentry, mode, 0); + if (retval != 0) + goto free_mem; + + fid = v9fs_fid_get_created(dentry); + if (!fid) { + dprintk(DEBUG_ERROR, "couldn't resolve fid from dentry\n"); + goto free_mem; + } + + /* issue a Twstat */ + v9fs_blank_wstat(&wstat); + wstat.muid = v9ses->name; + wstat.extension = (char *) extension; + retval = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall); + if (retval < 0) { + PRINT_FCALL_ERROR("wstat error", fcall); + goto free_mem; + } + + err = v9fs_t_clunk(v9ses, fid->fid); + if (err < 0) { + dprintk(DEBUG_ERROR, "clunk failed: %d\n", err); + goto free_mem; + } + + d_drop(dentry); /* FID - will this also clunk? */ + +free_mem: + kfree(fcall); + return retval; +} + +/** + * v9fs_vfs_symlink - helper function to create symlinks + * @dir: directory inode containing symlink + * @dentry: dentry for symlink + * @symname: symlink data + * + * See 9P2000.u RFC for more information + * + */ + +static int +v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) +{ + dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, + symname); + + return v9fs_vfs_mkspecial(dir, dentry, S_IFLNK, symname); +} + /** * v9fs_vfs_link - create a hardlink * @old_dentry: dentry for file to link to @@ -1173,64 +1066,24 @@ static int v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { - int retval = -EPERM; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); - struct v9fs_fcall *fcall = NULL; - struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); - struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); - struct v9fs_fid *newfid = NULL; - char *symname = __getname(); + int retval; + struct v9fs_fid *oldfid; + char *name; dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, old_dentry->d_name.name); - if (!v9ses->extended) { - dprintk(DEBUG_ERROR, "not extended\n"); - goto FreeMem; - } - - /* get fid of old_dentry */ - sprintf(symname, "hardlink(%d)\n", oldfid->fid); - - /* issue a create */ - retval = v9fs_create(dir, dentry, V9FS_DMLINK, 0); - if (retval != 0) - goto FreeMem; - - newfid = v9fs_fid_lookup(dentry); - if (!newfid) { - dprintk(DEBUG_ERROR, "couldn't resolve fid from dentry\n"); - goto FreeMem; - } - - /* issue a twstat */ - v9fs_blank_mistat(v9ses, mistat); - strcpy(mistat->data + 1, symname); - mistat->extension = mistat->data + 1; - retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); - if (retval < 0) { - dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", - FCALL_ERROR(fcall)); - goto FreeMem; - } - - kfree(fcall); - - if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { - dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", - FCALL_ERROR(fcall)); - goto FreeMem; + oldfid = v9fs_fid_lookup(old_dentry); + if (!oldfid) { + dprintk(DEBUG_ERROR, "can't find oldfid\n"); + return -EPERM; } - d_drop(dentry); /* FID - will this also clunk? */ - - kfree(fcall); - fcall = NULL; + name = __getname(); + sprintf(name, "hardlink(%d)\n", oldfid->fid); + retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name); + __putname(name); - FreeMem: - kfree(mistat); - kfree(fcall); - __putname(symname); return retval; } @@ -1246,82 +1099,30 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, static int v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { - int retval = -EPERM; - struct v9fs_fid *newfid; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); - struct v9fs_fcall *fcall = NULL; - struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); - char *symname = __getname(); + int retval; + char *name; dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); - if (!mistat) - return -ENOMEM; - - if (!new_valid_dev(rdev)) { - retval = -EINVAL; - goto FreeMem; - } - - if (!v9ses->extended) { - dprintk(DEBUG_ERROR, "not extended\n"); - goto FreeMem; - } - - /* issue a create */ - retval = v9fs_create(dir, dentry, mode, 0); - - if (retval != 0) - goto FreeMem; - - newfid = v9fs_fid_lookup(dentry); - if (!newfid) { - dprintk(DEBUG_ERROR, "coudn't resove fid from dentry\n"); - retval = -EINVAL; - goto FreeMem; - } + if (!new_valid_dev(rdev)) + return -EINVAL; + name = __getname(); /* build extension */ if (S_ISBLK(mode)) - sprintf(symname, "b %u %u", MAJOR(rdev), MINOR(rdev)); + sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev)); else if (S_ISCHR(mode)) - sprintf(symname, "c %u %u", MAJOR(rdev), MINOR(rdev)); + sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev)); else if (S_ISFIFO(mode)) - ; /* DO NOTHING */ + *name = 0; else { - retval = -EINVAL; - goto FreeMem; - } - - if (!S_ISFIFO(mode)) { - /* issue a twstat */ - v9fs_blank_mistat(v9ses, mistat); - strcpy(mistat->data + 1, symname); - mistat->extension = mistat->data + 1; - retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); - if (retval < 0) { - dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", - FCALL_ERROR(fcall)); - goto FreeMem; - } + __putname(name); + return -EINVAL; } - /* need to update dcache so we show up */ - kfree(fcall); - - if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { - dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", - FCALL_ERROR(fcall)); - goto FreeMem; - } - - d_drop(dentry); /* FID - will this also clunk? */ - - FreeMem: - kfree(mistat); - kfree(fcall); - __putname(symname); + retval = v9fs_vfs_mkspecial(dir, dentry, mode, name); + __putname(name); return retval; } diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 82c5b00..ae0f06b 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -44,7 +44,6 @@ #include "v9fs.h" #include "9p.h" #include "v9fs_vfs.h" -#include "conv.h" #include "fid.h" static void v9fs_clear_inode(struct inode *); @@ -123,12 +122,13 @@ static struct super_block *v9fs_get_sb(struct file_system_type dprintk(DEBUG_VFS, " \n"); - v9ses = kcalloc(1, sizeof(struct v9fs_session_info), GFP_KERNEL); + v9ses = kzalloc(sizeof(struct v9fs_session_info), GFP_KERNEL); if (!v9ses) return ERR_PTR(-ENOMEM); if ((newfid = v9fs_session_init(v9ses, dev_name, data)) < 0) { dprintk(DEBUG_ERROR, "problem initiating session\n"); + kfree(v9ses); return ERR_PTR(newfid); } @@ -157,7 +157,7 @@ static struct super_block *v9fs_get_sb(struct file_system_type stat_result = v9fs_t_stat(v9ses, newfid, &fcall); if (stat_result < 0) { dprintk(DEBUG_ERROR, "stat error\n"); - v9fs_t_clunk(v9ses, newfid, NULL); + v9fs_t_clunk(v9ses, newfid); v9fs_put_idpool(newfid, &v9ses->fidpool); } else { /* Setup the Root Inode */ @@ -167,10 +167,10 @@ static struct super_block *v9fs_get_sb(struct file_system_type goto put_back_sb; } - root_fid->qid = fcall->params.rstat.stat->qid; + root_fid->qid = fcall->params.rstat.stat.qid; root->d_inode->i_ino = - v9fs_qid2ino(&fcall->params.rstat.stat->qid); - v9fs_mistat2inode(fcall->params.rstat.stat, root->d_inode, sb); + v9fs_qid2ino(&fcall->params.rstat.stat.qid); + v9fs_stat2inode(&fcall->params.rstat.stat, root->d_inode, sb); } kfree(fcall); diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt index 175b2e8..f3d3d81 100644 --- a/fs/Kconfig.binfmt +++ b/fs/Kconfig.binfmt @@ -1,6 +1,6 @@ config BINFMT_ELF bool "Kernel support for ELF binaries" - depends on MMU + depends on MMU && (BROKEN || !FRV) default y ---help--- ELF (Executable and Linkable Format) is a format for libraries and diff --git a/fs/Makefile b/fs/Makefile index 7367611..35e9aec 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -10,7 +10,7 @@ obj-y := open.o read_write.o file_table.o buffer.o bio.o super.o \ ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ - ioprio.o pnode.o + ioprio.o pnode.o drop_caches.o obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_EPOLL) += eventpoll.o diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 6682d6d..5c61c24 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -137,7 +137,7 @@ static inline void afs_dir_check_page(struct inode *dir, struct page *page) #endif /* determine how many magic numbers there should be in this page */ - latter = dir->i_size - (page->index << PAGE_CACHE_SHIFT); + latter = dir->i_size - page_offset(page); if (latter >= PAGE_SIZE) qty = PAGE_SIZE; else diff --git a/fs/afs/volume.h b/fs/afs/volume.h index 1e69188..bfdcf19 100644 --- a/fs/afs/volume.h +++ b/fs/afs/volume.h @@ -18,8 +18,6 @@ #include "kafsasyncd.h" #include "cache.h" -#define __packed __attribute__((packed)) - typedef enum { AFS_VLUPD_SLEEP, /* sleeping waiting for update timer to fire */ AFS_VLUPD_PENDING, /* on pending queue */ @@ -115,7 +113,7 @@ struct afs_volume struct cachefs_cookie *cache; /* caching cookie */ #endif afs_volid_t vid; /* volume ID */ - afs_voltype_t __packed type; /* type of volume */ + afs_voltype_t type; /* type of volume */ char type_force; /* force volume type (suppress R/O -> R/W) */ unsigned short nservers; /* number of server slots filled */ unsigned short rjservers; /* number of servers discarded due to -ENOMEDIUM */ @@ -29,7 +29,6 @@ #include <linux/highmem.h> #include <linux/workqueue.h> #include <linux/security.h> -#include <linux/rcuref.h> #include <asm/kmap_types.h> #include <asm/uaccess.h> @@ -514,7 +513,7 @@ static int __aio_put_req(struct kioctx *ctx, struct kiocb *req) /* Must be done under the lock to serialise against cancellation. * Call this aio_fput as it duplicates fput via the fput_work. */ - if (unlikely(rcuref_dec_and_test(&req->ki_filp->f_count))) { + if (unlikely(atomic_dec_and_test(&req->ki_filp->f_count))) { get_ioctx(ctx); spin_lock(&fput_lock); list_add(&req->ki_list, &fput_head); @@ -67,20 +67,12 @@ EXPORT_SYMBOL(inode_change_ok); int inode_setattr(struct inode * inode, struct iattr * attr) { unsigned int ia_valid = attr->ia_valid; - int error = 0; - - if (ia_valid & ATTR_SIZE) { - if (attr->ia_size != i_size_read(inode)) { - error = vmtruncate(inode, attr->ia_size); - if (error || (ia_valid == ATTR_SIZE)) - goto out; - } else { - /* - * We skipped the truncate but must still update - * timestamps - */ - ia_valid |= ATTR_MTIME|ATTR_CTIME; - } + + if (ia_valid & ATTR_SIZE && + attr->ia_size != i_size_read(inode)) { + int error = vmtruncate(inode, attr->ia_size); + if (error) + return error; } if (ia_valid & ATTR_UID) @@ -104,8 +96,8 @@ int inode_setattr(struct inode * inode, struct iattr * attr) inode->i_mode = mode; } mark_inode_dirty(inode); -out: - return error; + + return 0; } EXPORT_SYMBOL(inode_setattr); diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index fca83e2..385bed0 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -209,7 +209,7 @@ static inline int simple_empty_nolock(struct dentry *dentry) struct dentry *child; int ret = 0; - list_for_each_entry(child, &dentry->d_subdirs, d_child) + list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) if (simple_positive(child)) goto out; ret = 1; diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index feb6ac4..dc39589 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -105,7 +105,7 @@ repeat: next = this_parent->d_subdirs.next; resume: while (next != &this_parent->d_subdirs) { - struct dentry *dentry = list_entry(next, struct dentry, d_child); + struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ if (!simple_positive(dentry)) { @@ -138,7 +138,7 @@ resume: } if (this_parent != top) { - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; this_parent = this_parent->d_parent; goto resume; } @@ -163,7 +163,7 @@ repeat: next = this_parent->d_subdirs.next; resume: while (next != &this_parent->d_subdirs) { - struct dentry *dentry = list_entry(next, struct dentry, d_child); + struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ if (!simple_positive(dentry)) { @@ -199,7 +199,7 @@ cont: } if (this_parent != parent) { - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; this_parent = this_parent->d_parent; goto resume; } @@ -238,7 +238,7 @@ static struct dentry *autofs4_expire(struct super_block *sb, /* On exit from the loop expire is set to a dgot dentry * to expire or it's NULL */ while ( next != &root->d_subdirs ) { - struct dentry *dentry = list_entry(next, struct dentry, d_child); + struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ if ( !simple_positive(dentry) ) { @@ -302,7 +302,7 @@ next: expired, (int)expired->d_name.len, expired->d_name.name); spin_lock(&dcache_lock); list_del(&expired->d_parent->d_subdirs); - list_add(&expired->d_parent->d_subdirs, &expired->d_child); + list_add(&expired->d_parent->d_subdirs, &expired->d_u.d_child); spin_unlock(&dcache_lock); return expired; } diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 818b37b..2d30828 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -91,7 +91,7 @@ repeat: next = this_parent->d_subdirs.next; resume: while (next != &this_parent->d_subdirs) { - struct dentry *dentry = list_entry(next, struct dentry, d_child); + struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - don`t care */ if (!simple_positive(dentry)) { @@ -117,7 +117,7 @@ resume: if (this_parent != sbi->root) { struct dentry *dentry = this_parent; - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; this_parent = this_parent->d_parent; spin_unlock(&dcache_lock); DPRINTK("parent dentry %p %.*s", diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 2a771ec..2241405 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -143,7 +143,8 @@ static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t f } while(1) { - struct dentry *de = list_entry(list, struct dentry, d_child); + struct dentry *de = list_entry(list, + struct dentry, d_u.d_child); if (!d_unhashed(de) && de->d_inode) { spin_unlock(&dcache_lock); diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index f36f221..80ca932 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -58,7 +58,7 @@ extern int dump_fpu (struct pt_regs *, elf_fpregset_t *); * If we don't support core dumping, then supply a NULL so we * don't even try. */ -#ifdef USE_ELF_CORE_DUMP +#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE) static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file); #else #define elf_core_dump NULL @@ -288,11 +288,17 @@ static unsigned long elf_map(struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type) { unsigned long map_addr; + unsigned long pageoffset = ELF_PAGEOFFSET(eppnt->p_vaddr); down_write(¤t->mm->mmap_sem); - map_addr = do_mmap(filep, ELF_PAGESTART(addr), - eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type, - eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr)); + /* mmap() will return -EINVAL if given a zero size, but a + * segment with zero filesize is perfectly valid */ + if (eppnt->p_filesz + pageoffset) + map_addr = do_mmap(filep, ELF_PAGESTART(addr), + eppnt->p_filesz + pageoffset, prot, type, + eppnt->p_offset - pageoffset); + else + map_addr = ELF_PAGESTART(addr); up_write(¤t->mm->mmap_sem); return(map_addr); } @@ -1107,7 +1113,7 @@ out: * Note that some platforms still use traditional core dumps and not * the ELF core dump. Each platform can select it as appropriate. */ -#ifdef USE_ELF_CORE_DUMP +#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE) /* * ELF core dumper diff --git a/fs/buffer.c b/fs/buffer.c index 5287be1..55f0975a 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -153,14 +153,8 @@ int sync_blockdev(struct block_device *bdev) { int ret = 0; - if (bdev) { - int err; - - ret = filemap_fdatawrite(bdev->bd_inode->i_mapping); - err = filemap_fdatawait(bdev->bd_inode->i_mapping); - if (!ret) - ret = err; - } + if (bdev) + ret = filemap_write_and_wait(bdev->bd_inode->i_mapping); return ret; } EXPORT_SYMBOL(sync_blockdev); @@ -1768,7 +1762,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, * handle that here by just cleaning them. */ - block = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); + block = (sector_t)page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); head = page_buffers(page); bh = head; @@ -2160,11 +2154,12 @@ int block_read_full_page(struct page *page, get_block_t *get_block) * truncates. Uses prepare/commit_write to allow the filesystem to * deal with the hole. */ -int generic_cont_expand(struct inode *inode, loff_t size) +static int __generic_cont_expand(struct inode *inode, loff_t size, + pgoff_t index, unsigned int offset) { struct address_space *mapping = inode->i_mapping; struct page *page; - unsigned long index, offset, limit; + unsigned long limit; int err; err = -EFBIG; @@ -2176,24 +2171,24 @@ int generic_cont_expand(struct inode *inode, loff_t size) if (size > inode->i_sb->s_maxbytes) goto out; - offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */ - - /* ugh. in prepare/commit_write, if from==to==start of block, we - ** skip the prepare. make sure we never send an offset for the start - ** of a block - */ - if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) { - offset++; - } - index = size >> PAGE_CACHE_SHIFT; err = -ENOMEM; page = grab_cache_page(mapping, index); if (!page) goto out; err = mapping->a_ops->prepare_write(NULL, page, offset, offset); - if (!err) { - err = mapping->a_ops->commit_write(NULL, page, offset, offset); + if (err) { + /* + * ->prepare_write() may have instantiated a few blocks + * outside i_size. Trim these off again. + */ + unlock_page(page); + page_cache_release(page); + vmtruncate(inode, inode->i_size); + goto out; } + + err = mapping->a_ops->commit_write(NULL, page, offset, offset); + unlock_page(page); page_cache_release(page); if (err > 0) @@ -2202,6 +2197,36 @@ out: return err; } +int generic_cont_expand(struct inode *inode, loff_t size) +{ + pgoff_t index; + unsigned int offset; + + offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */ + + /* ugh. in prepare/commit_write, if from==to==start of block, we + ** skip the prepare. make sure we never send an offset for the start + ** of a block + */ + if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) { + /* caller must handle this extra byte. */ + offset++; + } + index = size >> PAGE_CACHE_SHIFT; + + return __generic_cont_expand(inode, size, index, offset); +} + +int generic_cont_expand_simple(struct inode *inode, loff_t size) +{ + loff_t pos = size - 1; + pgoff_t index = pos >> PAGE_CACHE_SHIFT; + unsigned int offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1; + + /* prepare/commit_write can handle even if from==to==start of block. */ + return __generic_cont_expand(inode, size, index, offset); +} + /* * For moronic filesystems that do not allow holes in file. * We may have to extend the file. @@ -2610,7 +2635,7 @@ int block_truncate_page(struct address_space *mapping, pgoff_t index = from >> PAGE_CACHE_SHIFT; unsigned offset = from & (PAGE_CACHE_SIZE-1); unsigned blocksize; - pgoff_t iblock; + sector_t iblock; unsigned length, pos; struct inode *inode = mapping->host; struct page *page; @@ -2626,7 +2651,7 @@ int block_truncate_page(struct address_space *mapping, return 0; length = blocksize - length; - iblock = index << (PAGE_CACHE_SHIFT - inode->i_blkbits); + iblock = (sector_t)index << (PAGE_CACHE_SHIFT - inode->i_blkbits); page = grab_cache_page(mapping, index); err = -ENOMEM; @@ -3145,6 +3170,7 @@ EXPORT_SYMBOL(fsync_bdev); EXPORT_SYMBOL(generic_block_bmap); EXPORT_SYMBOL(generic_commit_write); EXPORT_SYMBOL(generic_cont_expand); +EXPORT_SYMBOL(generic_cont_expand_simple); EXPORT_SYMBOL(init_buffer); EXPORT_SYMBOL(invalidate_bdev); EXPORT_SYMBOL(ll_rw_block); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 14a1c72..5ade53d 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -127,8 +127,7 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, if (file->f_dentry->d_inode->i_mapping) { /* BB no need to lock inode until after invalidate since namei code should already have it locked? */ - filemap_fdatawrite(file->f_dentry->d_inode->i_mapping); - filemap_fdatawait(file->f_dentry->d_inode->i_mapping); + filemap_write_and_wait(file->f_dentry->d_inode->i_mapping); } cFYI(1, ("invalidating remote inode since open detected it " "changed")); @@ -419,8 +418,7 @@ static int cifs_reopen_file(struct inode *inode, struct file *file, pCifsInode = CIFS_I(inode); if (pCifsInode) { if (can_flush) { - filemap_fdatawrite(inode->i_mapping); - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); /* temporarily disable caching while we go to server to get inode info */ pCifsInode->clientCanCacheAll = FALSE; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 411c1f7..9558f51 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1148,8 +1148,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) /* BB check if we need to refresh inode from server now ? BB */ /* need to flush data before changing file size on server */ - filemap_fdatawrite(direntry->d_inode->i_mapping); - filemap_fdatawait(direntry->d_inode->i_mapping); + filemap_write_and_wait(direntry->d_inode->i_mapping); if (attrs->ia_valid & ATTR_SIZE) { /* To avoid spurious oplock breaks from server, in the case of diff --git a/fs/coda/cache.c b/fs/coda/cache.c index 80072fd..c607d92 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -93,7 +93,7 @@ static void coda_flag_children(struct dentry *parent, int flag) spin_lock(&dcache_lock); list_for_each(child, &parent->d_subdirs) { - de = list_entry(child, struct dentry, d_child); + de = list_entry(child, struct dentry, d_u.d_child); /* don't know what to do with negative dentries */ if ( ! de->d_inode ) continue; diff --git a/fs/compat.c b/fs/compat.c index 55ac032..271b75d 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -494,9 +494,21 @@ asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, ret = sys_fcntl(fd, cmd, (unsigned long)&f); set_fs(old_fs); if (cmd == F_GETLK && ret == 0) { - if ((f.l_start >= COMPAT_OFF_T_MAX) || - ((f.l_start + f.l_len) > COMPAT_OFF_T_MAX)) + /* GETLK was successfule and we need to return the data... + * but it needs to fit in the compat structure. + * l_start shouldn't be too big, unless the original + * start + end is greater than COMPAT_OFF_T_MAX, in which + * case the app was asking for trouble, so we return + * -EOVERFLOW in that case. + * l_len could be too big, in which case we just truncate it, + * and only allow the app to see that part of the conflicting + * lock that might make sense to it anyway + */ + + if (f.l_start > COMPAT_OFF_T_MAX) ret = -EOVERFLOW; + if (f.l_len > COMPAT_OFF_T_MAX) + f.l_len = COMPAT_OFF_T_MAX; if (ret == 0) ret = put_compat_flock(&f, compat_ptr(arg)); } @@ -515,9 +527,11 @@ asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, (unsigned long)&f); set_fs(old_fs); if (cmd == F_GETLK64 && ret == 0) { - if ((f.l_start >= COMPAT_LOFF_T_MAX) || - ((f.l_start + f.l_len) > COMPAT_LOFF_T_MAX)) + /* need to return lock information - see above for commentary */ + if (f.l_start > COMPAT_LOFF_T_MAX) ret = -EOVERFLOW; + if (f.l_len > COMPAT_LOFF_T_MAX) + f.l_len = COMPAT_LOFF_T_MAX; if (ret == 0) ret = put_compat_flock64(&f, compat_ptr(arg)); } diff --git a/fs/dcache.c b/fs/dcache.c index 17e43913..1536f15 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -71,7 +71,7 @@ struct dentry_stat_t dentry_stat = { static void d_callback(struct rcu_head *head) { - struct dentry * dentry = container_of(head, struct dentry, d_rcu); + struct dentry * dentry = container_of(head, struct dentry, d_u.d_rcu); if (dname_external(dentry)) kfree(dentry->d_name.name); @@ -86,7 +86,7 @@ static void d_free(struct dentry *dentry) { if (dentry->d_op && dentry->d_op->d_release) dentry->d_op->d_release(dentry); - call_rcu(&dentry->d_rcu, d_callback); + call_rcu(&dentry->d_u.d_rcu, d_callback); } /* @@ -193,7 +193,7 @@ kill_it: { list_del(&dentry->d_lru); dentry_stat.nr_unused--; } - list_del(&dentry->d_child); + list_del(&dentry->d_u.d_child); dentry_stat.nr_dentry--; /* For d_free, below */ /*drops the locks, at that point nobody can reach this dentry */ dentry_iput(dentry); @@ -367,7 +367,7 @@ static inline void prune_one_dentry(struct dentry * dentry) struct dentry * parent; __d_drop(dentry); - list_del(&dentry->d_child); + list_del(&dentry->d_u.d_child); dentry_stat.nr_dentry--; /* For d_free, below */ dentry_iput(dentry); parent = dentry->d_parent; @@ -518,7 +518,7 @@ repeat: resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; - struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); next = tmp->next; /* Have we found a mount point ? */ if (d_mountpoint(dentry)) @@ -532,7 +532,7 @@ resume: * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; this_parent = this_parent->d_parent; goto resume; } @@ -569,7 +569,7 @@ repeat: resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; - struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); next = tmp->next; if (!list_empty(&dentry->d_lru)) { @@ -610,7 +610,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, found); * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; this_parent = this_parent->d_parent; #ifdef DCACHE_DEBUG printk(KERN_DEBUG "select_parent: ascending to %s/%s, found=%d\n", @@ -753,12 +753,12 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) dentry->d_parent = dget(parent); dentry->d_sb = parent->d_sb; } else { - INIT_LIST_HEAD(&dentry->d_child); + INIT_LIST_HEAD(&dentry->d_u.d_child); } spin_lock(&dcache_lock); if (parent) - list_add(&dentry->d_child, &parent->d_subdirs); + list_add(&dentry->d_u.d_child, &parent->d_subdirs); dentry_stat.nr_dentry++; spin_unlock(&dcache_lock); @@ -1310,8 +1310,8 @@ already_unhashed: /* Unhash the target: dput() will then get rid of it */ __d_drop(target); - list_del(&dentry->d_child); - list_del(&target->d_child); + list_del(&dentry->d_u.d_child); + list_del(&target->d_u.d_child); /* Switch the names.. */ switch_names(dentry, target); @@ -1322,15 +1322,15 @@ already_unhashed: if (IS_ROOT(dentry)) { dentry->d_parent = target->d_parent; target->d_parent = target; - INIT_LIST_HEAD(&target->d_child); + INIT_LIST_HEAD(&target->d_u.d_child); } else { do_switch(dentry->d_parent, target->d_parent); /* And add them back to the (new) parent lists */ - list_add(&target->d_child, &target->d_parent->d_subdirs); + list_add(&target->d_u.d_child, &target->d_parent->d_subdirs); } - list_add(&dentry->d_child, &dentry->d_parent->d_subdirs); + list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs); spin_unlock(&target->d_lock); spin_unlock(&dentry->d_lock); write_sequnlock(&rename_lock); @@ -1568,7 +1568,7 @@ repeat: resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; - struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); next = tmp->next; if (d_unhashed(dentry)||!dentry->d_inode) continue; @@ -1579,7 +1579,7 @@ resume: atomic_dec(&dentry->d_count); } if (this_parent != root) { - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; atomic_dec(&this_parent->d_count); this_parent = this_parent->d_parent; goto resume; diff --git a/fs/drop_caches.c b/fs/drop_caches.c new file mode 100644 index 0000000..4e47623 --- /dev/null +++ b/fs/drop_caches.c @@ -0,0 +1,68 @@ +/* + * Implement the manual drop-all-pagecache function + */ + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/writeback.h> +#include <linux/sysctl.h> +#include <linux/gfp.h> + +/* A global variable is a bit ugly, but it keeps the code simple */ +int sysctl_drop_caches; + +static void drop_pagecache_sb(struct super_block *sb) +{ + struct inode *inode; + + spin_lock(&inode_lock); + list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { + if (inode->i_state & (I_FREEING|I_WILL_FREE)) + continue; + invalidate_inode_pages(inode->i_mapping); + } + spin_unlock(&inode_lock); +} + +void drop_pagecache(void) +{ + struct super_block *sb; + + spin_lock(&sb_lock); +restart: + list_for_each_entry(sb, &super_blocks, s_list) { + sb->s_count++; + spin_unlock(&sb_lock); + down_read(&sb->s_umount); + if (sb->s_root) + drop_pagecache_sb(sb); + up_read(&sb->s_umount); + spin_lock(&sb_lock); + if (__put_super_and_need_restart(sb)) + goto restart; + } + spin_unlock(&sb_lock); +} + +void drop_slab(void) +{ + int nr_objects; + + do { + nr_objects = shrink_slab(1000, GFP_KERNEL, 1000); + } while (nr_objects > 10); +} + +int drop_caches_sysctl_handler(ctl_table *table, int write, + struct file *file, void __user *buffer, size_t *length, loff_t *ppos) +{ + proc_dointvec_minmax(table, write, file, buffer, length, ppos); + if (write) { + if (sysctl_drop_caches & 1) + drop_pagecache(); + if (sysctl_drop_caches & 2) + drop_slab(); + } + return 0; +} @@ -760,7 +760,7 @@ no_thread_group: spin_lock(&oldsighand->siglock); spin_lock(&newsighand->siglock); - current->sighand = newsighand; + rcu_assign_pointer(current->sighand, newsighand); recalc_sigpending(); spin_unlock(&newsighand->siglock); @@ -768,7 +768,7 @@ no_thread_group: write_unlock_irq(&tasklist_lock); if (atomic_dec_and_test(&oldsighand->count)) - kmem_cache_free(sighand_cachep, oldsighand); + sighand_free(oldsighand); } BUG_ON(!thread_group_leader(current)); @@ -1462,6 +1462,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) if (!(current->signal->flags & SIGNAL_GROUP_EXIT)) { current->signal->flags = SIGNAL_GROUP_EXIT; current->signal->group_exit_code = exit_code; + current->signal->group_stop_count = 0; retval = 0; } spin_unlock_irq(¤t->sighand->siglock); @@ -1477,7 +1478,6 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) * Clear any false indication of pending signals that might * be seen by the filesystem code called to write the core file. */ - current->signal->group_stop_count = 0; clear_thread_flag(TIF_SIGPENDING); if (current->signal->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump) @@ -1505,7 +1505,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) goto close_fail; if (!file->f_op->write) goto close_fail; - if (do_truncate(file->f_dentry, 0, file) != 0) + if (do_truncate(file->f_dentry, 0, 0, file) != 0) goto close_fail; retval = binfmt->core_dump(signr, regs, file); diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index 9e4a243..6907807 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c @@ -651,7 +651,7 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) /* Error cases - e2fsck has already cleaned up for us */ if (ino > max_ino) { ext3_warning(sb, __FUNCTION__, - "bad orphan ino %lu! e2fsck was run?\n", ino); + "bad orphan ino %lu! e2fsck was run?", ino); goto out; } @@ -660,7 +660,7 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) bitmap_bh = read_inode_bitmap(sb, block_group); if (!bitmap_bh) { ext3_warning(sb, __FUNCTION__, - "inode bitmap error for orphan %lu\n", ino); + "inode bitmap error for orphan %lu", ino); goto out; } @@ -672,7 +672,7 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) !(inode = iget(sb, ino)) || is_bad_inode(inode) || NEXT_ORPHAN(inode) > max_ino) { ext3_warning(sb, __FUNCTION__, - "bad orphan inode %lu! e2fsck was run?\n", ino); + "bad orphan inode %lu! e2fsck was run?", ino); printk(KERN_NOTICE "ext3_test_bit(bit=%d, block=%llu) = %d\n", bit, (unsigned long long)bitmap_bh->b_blocknr, ext3_test_bit(bit, bitmap_bh->b_data)); diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index b3c690a..af193a3 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1476,7 +1476,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, if (levels && (dx_get_count(frames->entries) == dx_get_limit(frames->entries))) { ext3_warning(sb, __FUNCTION__, - "Directory index full!\n"); + "Directory index full!"); err = -ENOSPC; goto cleanup; } diff --git a/fs/ext3/resize.c b/fs/ext3/resize.c index 6104ad3..1041dab 100644 --- a/fs/ext3/resize.c +++ b/fs/ext3/resize.c @@ -31,7 +31,7 @@ static int verify_group_input(struct super_block *sb, unsigned start = le32_to_cpu(es->s_blocks_count); unsigned end = start + input->blocks_count; unsigned group = input->group; - unsigned itend = input->inode_table + EXT3_SB(sb)->s_itb_per_group; + unsigned itend = input->inode_table + sbi->s_itb_per_group; unsigned overhead = ext3_bg_has_super(sb, group) ? (1 + ext3_bg_num_gdb(sb, group) + le16_to_cpu(es->s_reserved_gdt_blocks)) : 0; @@ -340,7 +340,7 @@ static int verify_reserved_gdb(struct super_block *sb, while ((grp = ext3_list_backups(sb, &three, &five, &seven)) < end) { if (le32_to_cpu(*p++) != grp * EXT3_BLOCKS_PER_GROUP(sb) + blk){ ext3_warning(sb, __FUNCTION__, - "reserved GDT %ld missing grp %d (%ld)\n", + "reserved GDT %ld missing grp %d (%ld)", blk, grp, grp * EXT3_BLOCKS_PER_GROUP(sb) + blk); return -EINVAL; @@ -393,7 +393,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, if (EXT3_SB(sb)->s_sbh->b_blocknr != le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block)) { ext3_warning(sb, __FUNCTION__, - "won't resize using backup superblock at %llu\n", + "won't resize using backup superblock at %llu", (unsigned long long)EXT3_SB(sb)->s_sbh->b_blocknr); return -EPERM; } @@ -417,7 +417,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, data = (__u32 *)dind->b_data; if (le32_to_cpu(data[gdb_num % EXT3_ADDR_PER_BLOCK(sb)]) != gdblock) { ext3_warning(sb, __FUNCTION__, - "new group %u GDT block %lu not reserved\n", + "new group %u GDT block %lu not reserved", input->group, gdblock); err = -EINVAL; goto exit_dind; @@ -540,7 +540,7 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode, for (res = 0; res < reserved_gdb; res++, blk++) { if (le32_to_cpu(*data) != blk) { ext3_warning(sb, __FUNCTION__, - "reserved block %lu not at offset %ld\n", + "reserved block %lu not at offset %ld", blk, (long)(data - (__u32 *)dind->b_data)); err = -EINVAL; goto exit_bh; @@ -683,7 +683,7 @@ exit_err: if (err) { ext3_warning(sb, __FUNCTION__, "can't update backup for group %d (err %d), " - "forcing fsck on next reboot\n", group, err); + "forcing fsck on next reboot", group, err); sbi->s_mount_state &= ~EXT3_VALID_FS; sbi->s_es->s_state &= ~cpu_to_le16(EXT3_VALID_FS); mark_buffer_dirty(sbi->s_sbh); @@ -722,7 +722,7 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) if (gdb_off == 0 && !EXT3_HAS_RO_COMPAT_FEATURE(sb, EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER)) { ext3_warning(sb, __FUNCTION__, - "Can't resize non-sparse filesystem further\n"); + "Can't resize non-sparse filesystem further"); return -EPERM; } @@ -730,13 +730,13 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) if (!EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_RESIZE_INODE)){ ext3_warning(sb, __FUNCTION__, - "No reserved GDT blocks, can't resize\n"); + "No reserved GDT blocks, can't resize"); return -EPERM; } inode = iget(sb, EXT3_RESIZE_INO); if (!inode || is_bad_inode(inode)) { ext3_warning(sb, __FUNCTION__, - "Error opening resize inode\n"); + "Error opening resize inode"); iput(inode); return -ENOENT; } @@ -764,9 +764,9 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) } lock_super(sb); - if (input->group != EXT3_SB(sb)->s_groups_count) { + if (input->group != sbi->s_groups_count) { ext3_warning(sb, __FUNCTION__, - "multiple resizers run on filesystem!\n"); + "multiple resizers run on filesystem!"); err = -EBUSY; goto exit_journal; } @@ -799,7 +799,7 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) * data. So we need to be careful to set all of the relevant * group descriptor data etc. *before* we enable the group. * - * The key field here is EXT3_SB(sb)->s_groups_count: as long as + * The key field here is sbi->s_groups_count: as long as * that retains its old value, nobody is going to access the new * group. * @@ -859,7 +859,7 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) smp_wmb(); /* Update the global fs size fields */ - EXT3_SB(sb)->s_groups_count++; + sbi->s_groups_count++; ext3_journal_dirty_metadata(handle, primary); @@ -874,7 +874,7 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) percpu_counter_mod(&sbi->s_freeinodes_counter, EXT3_INODES_PER_GROUP(sb)); - ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh); + ext3_journal_dirty_metadata(handle, sbi->s_sbh); sb->s_dirt = 1; exit_journal: @@ -937,7 +937,7 @@ int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es, if (last == 0) { ext3_warning(sb, __FUNCTION__, - "need to use ext2online to resize further\n"); + "need to use ext2online to resize further"); return -EPERM; } @@ -973,7 +973,7 @@ int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es, lock_super(sb); if (o_blocks_count != le32_to_cpu(es->s_blocks_count)) { ext3_warning(sb, __FUNCTION__, - "multiple resizers run on filesystem!\n"); + "multiple resizers run on filesystem!"); err = -EBUSY; goto exit_put; } diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 4e67306..7c45acf 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -43,7 +43,8 @@ #include "acl.h" #include "namei.h" -static int ext3_load_journal(struct super_block *, struct ext3_super_block *); +static int ext3_load_journal(struct super_block *, struct ext3_super_block *, + unsigned long journal_devnum); static int ext3_create_journal(struct super_block *, struct ext3_super_block *, int); static void ext3_commit_super (struct super_block * sb, @@ -628,7 +629,7 @@ enum { Opt_nouid32, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_reservation, Opt_noreservation, Opt_noload, Opt_nobh, - Opt_commit, Opt_journal_update, Opt_journal_inum, + Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota, @@ -666,6 +667,7 @@ static match_table_t tokens = { {Opt_commit, "commit=%u"}, {Opt_journal_update, "journal=update"}, {Opt_journal_inum, "journal=%u"}, + {Opt_journal_dev, "journal_dev=%u"}, {Opt_abort, "abort"}, {Opt_data_journal, "data=journal"}, {Opt_data_ordered, "data=ordered"}, @@ -705,8 +707,9 @@ static unsigned long get_sb_block(void **data) return sb_block; } -static int parse_options (char * options, struct super_block *sb, - unsigned long * inum, unsigned long *n_blocks_count, int is_remount) +static int parse_options (char *options, struct super_block *sb, + unsigned long *inum, unsigned long *journal_devnum, + unsigned long *n_blocks_count, int is_remount) { struct ext3_sb_info *sbi = EXT3_SB(sb); char * p; @@ -839,6 +842,16 @@ static int parse_options (char * options, struct super_block *sb, return 0; *inum = option; break; + case Opt_journal_dev: + if (is_remount) { + printk(KERN_ERR "EXT3-fs: cannot specify " + "journal on remount\n"); + return 0; + } + if (match_int(&args[0], &option)) + return 0; + *journal_devnum = option; + break; case Opt_noload: set_opt (sbi->s_mount_opt, NOLOAD); break; @@ -1331,6 +1344,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) unsigned long logic_sb_block; unsigned long offset = 0; unsigned long journal_inum = 0; + unsigned long journal_devnum = 0; unsigned long def_mount_opts; struct inode *root; int blocksize; @@ -1411,7 +1425,8 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) set_opt(sbi->s_mount_opt, RESERVATION); - if (!parse_options ((char *) data, sb, &journal_inum, NULL, 0)) + if (!parse_options ((char *) data, sb, &journal_inum, &journal_devnum, + NULL, 0)) goto failed_mount; sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | @@ -1622,7 +1637,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) */ if (!test_opt(sb, NOLOAD) && EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { - if (ext3_load_journal(sb, es)) + if (ext3_load_journal(sb, es, journal_devnum)) goto failed_mount2; } else if (journal_inum) { if (ext3_create_journal(sb, es, journal_inum)) @@ -1902,15 +1917,24 @@ out_bdev: return NULL; } -static int ext3_load_journal(struct super_block * sb, - struct ext3_super_block * es) +static int ext3_load_journal(struct super_block *sb, + struct ext3_super_block *es, + unsigned long journal_devnum) { journal_t *journal; int journal_inum = le32_to_cpu(es->s_journal_inum); - dev_t journal_dev = new_decode_dev(le32_to_cpu(es->s_journal_dev)); + dev_t journal_dev; int err = 0; int really_read_only; + if (journal_devnum && + journal_devnum != le32_to_cpu(es->s_journal_dev)) { + printk(KERN_INFO "EXT3-fs: external journal device major/minor " + "numbers have changed\n"); + journal_dev = new_decode_dev(journal_devnum); + } else + journal_dev = new_decode_dev(le32_to_cpu(es->s_journal_dev)); + really_read_only = bdev_read_only(sb->s_bdev); /* @@ -1969,6 +1993,16 @@ static int ext3_load_journal(struct super_block * sb, EXT3_SB(sb)->s_journal = journal; ext3_clear_journal_err(sb, es); + + if (journal_devnum && + journal_devnum != le32_to_cpu(es->s_journal_dev)) { + es->s_journal_dev = cpu_to_le32(journal_devnum); + sb->s_dirt = 1; + + /* Make sure we flush the recovery flag to disk. */ + ext3_commit_super(sb, es, 1); + } + return 0; } @@ -2197,7 +2231,7 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data) /* * Allow the "check" option to be passed as a remount option. */ - if (!parse_options(data, sb, NULL, &n_blocks_count, 1)) { + if (!parse_options(data, sb, NULL, NULL, &n_blocks_count, 1)) { err = -EINVAL; goto restore_opts; } diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 77c24fc..1acc941 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -295,7 +295,8 @@ static int fat_bmap_cluster(struct inode *inode, int cluster) return dclus; } -int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys) +int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, + unsigned long *mapped_blocks) { struct super_block *sb = inode->i_sb; struct msdos_sb_info *sbi = MSDOS_SB(sb); @@ -303,9 +304,12 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys) int cluster, offset; *phys = 0; + *mapped_blocks = 0; if ((sbi->fat_bits != 32) && (inode->i_ino == MSDOS_ROOT_INO)) { - if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits)) + if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits)) { *phys = sector + sbi->dir_start; + *mapped_blocks = 1; + } return 0; } last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1)) @@ -318,7 +322,11 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys) cluster = fat_bmap_cluster(inode, cluster); if (cluster < 0) return cluster; - else if (cluster) + else if (cluster) { *phys = fat_clus_to_blknr(sbi, cluster) + offset; + *mapped_blocks = sbi->sec_per_clus - offset; + if (*mapped_blocks > last_block - sector) + *mapped_blocks = last_block - sector; + } return 0; } diff --git a/fs/fat/dir.c b/fs/fat/dir.c index ba82496..eef1b81 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -45,8 +45,8 @@ static inline void fat_dir_readahead(struct inode *dir, sector_t iblock, if ((sbi->fat_bits != 32) && (dir->i_ino == MSDOS_ROOT_INO)) return; - bh = sb_getblk(sb, phys); - if (bh && !buffer_uptodate(bh)) { + bh = sb_find_get_block(sb, phys); + if (bh == NULL || !buffer_uptodate(bh)) { for (sec = 0; sec < sbi->sec_per_clus; sec++) sb_breadahead(sb, phys + sec); } @@ -68,8 +68,8 @@ static int fat__get_entry(struct inode *dir, loff_t *pos, { struct super_block *sb = dir->i_sb; sector_t phys, iblock; - int offset; - int err; + unsigned long mapped_blocks; + int err, offset; next: if (*bh) @@ -77,7 +77,7 @@ next: *bh = NULL; iblock = *pos >> sb->s_blocksize_bits; - err = fat_bmap(dir, iblock, &phys); + err = fat_bmap(dir, iblock, &phys, &mapped_blocks); if (err || !phys) return -1; /* beyond EOF or error */ @@ -418,7 +418,7 @@ EODir: return err; } -EXPORT_SYMBOL(fat_search_long); +EXPORT_SYMBOL_GPL(fat_search_long); struct fat_ioctl_filldir_callback { struct dirent __user *dirent; @@ -780,7 +780,7 @@ int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, return -ENOENT; } -EXPORT_SYMBOL(fat_get_dotdot_entry); +EXPORT_SYMBOL_GPL(fat_get_dotdot_entry); /* See if directory is empty */ int fat_dir_empty(struct inode *dir) @@ -803,7 +803,7 @@ int fat_dir_empty(struct inode *dir) return result; } -EXPORT_SYMBOL(fat_dir_empty); +EXPORT_SYMBOL_GPL(fat_dir_empty); /* * fat_subdirs counts the number of sub-directories of dir. It can be run @@ -849,7 +849,7 @@ int fat_scan(struct inode *dir, const unsigned char *name, return -ENOENT; } -EXPORT_SYMBOL(fat_scan); +EXPORT_SYMBOL_GPL(fat_scan); static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots) { @@ -936,7 +936,7 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo) return 0; } -EXPORT_SYMBOL(fat_remove_entries); +EXPORT_SYMBOL_GPL(fat_remove_entries); static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used, struct buffer_head **bhs, int nr_bhs) @@ -1048,7 +1048,7 @@ error: return err; } -EXPORT_SYMBOL(fat_alloc_new_dir); +EXPORT_SYMBOL_GPL(fat_alloc_new_dir); static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, int *nr_cluster, struct msdos_dir_entry **de, @@ -1264,4 +1264,4 @@ error_remove: return err; } -EXPORT_SYMBOL(fat_add_entries); +EXPORT_SYMBOL_GPL(fat_add_entries); diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 4164cd5..a1a9e04 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -476,6 +476,7 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) sbi->prev_free = entry; if (sbi->free_clusters != -1) sbi->free_clusters--; + sb->s_dirt = 1; cluster[idx_clus] = entry; idx_clus++; @@ -496,6 +497,7 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) /* Couldn't allocate the free entries */ sbi->free_clusters = 0; + sb->s_dirt = 1; err = -ENOSPC; out: @@ -509,7 +511,6 @@ out: } for (i = 0; i < nr_bhs; i++) brelse(bhs[i]); - fat_clusters_flush(sb); if (err && idx_clus) fat_free_clusters(inode, cluster[0]); @@ -542,8 +543,10 @@ int fat_free_clusters(struct inode *inode, int cluster) } ops->ent_put(&fatent, FAT_ENT_FREE); - if (sbi->free_clusters != -1) + if (sbi->free_clusters != -1) { sbi->free_clusters++; + sb->s_dirt = 1; + } if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) { if (sb->s_flags & MS_SYNCHRONOUS) { @@ -578,7 +581,7 @@ error: return err; } -EXPORT_SYMBOL(fat_free_clusters); +EXPORT_SYMBOL_GPL(fat_free_clusters); int fat_count_free_clusters(struct super_block *sb) { @@ -605,6 +608,7 @@ int fat_count_free_clusters(struct super_block *sb) } while (fat_ent_next(sbi, &fatent)); } sbi->free_clusters = free; + sb->s_dirt = 1; fatent_brelse(&fatent); out: unlock_fat(sbi); diff --git a/fs/fat/file.c b/fs/fat/file.c index 7134403..9b07c32 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -11,6 +11,7 @@ #include <linux/msdos_fs.h> #include <linux/smp_lock.h> #include <linux/buffer_head.h> +#include <linux/writeback.h> int fat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) @@ -124,6 +125,24 @@ struct file_operations fat_file_operations = { .sendfile = generic_file_sendfile, }; +static int fat_cont_expand(struct inode *inode, loff_t size) +{ + struct address_space *mapping = inode->i_mapping; + loff_t start = inode->i_size, count = size - inode->i_size; + int err; + + err = generic_cont_expand_simple(inode, size); + if (err) + goto out; + + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + if (IS_SYNC(inode)) + err = sync_page_range_nolock(inode, mapping, start, count); +out: + return err; +} + int fat_notify_change(struct dentry *dentry, struct iattr *attr) { struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); @@ -132,11 +151,17 @@ int fat_notify_change(struct dentry *dentry, struct iattr *attr) lock_kernel(); - /* FAT cannot truncate to a longer file */ + /* + * Expand the file. Since inode_setattr() updates ->i_size + * before calling the ->truncate(), but FAT needs to fill the + * hole before it. + */ if (attr->ia_valid & ATTR_SIZE) { if (attr->ia_size > inode->i_size) { - error = -EPERM; - goto out; + error = fat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + goto out; + attr->ia_valid &= ~ATTR_SIZE; } } @@ -173,7 +198,7 @@ out: return error; } -EXPORT_SYMBOL(fat_notify_change); +EXPORT_SYMBOL_GPL(fat_notify_change); /* Free all clusters after the skip'th cluster. */ static int fat_free(struct inode *inode, int skip) diff --git a/fs/fat/inode.c b/fs/fat/inode.c index a0f9b9f..e7f4aa7 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -18,10 +18,12 @@ #include <linux/seq_file.h> #include <linux/msdos_fs.h> #include <linux/pagemap.h> +#include <linux/mpage.h> #include <linux/buffer_head.h> #include <linux/mount.h> #include <linux/vfs.h> #include <linux/parser.h> +#include <linux/uio.h> #include <asm/unaligned.h> #ifndef CONFIG_FAT_DEFAULT_IOCHARSET @@ -48,51 +50,97 @@ static int fat_add_cluster(struct inode *inode) return err; } -static int fat_get_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) +static int __fat_get_blocks(struct inode *inode, sector_t iblock, + unsigned long *max_blocks, + struct buffer_head *bh_result, int create) { struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); sector_t phys; - int err; + unsigned long mapped_blocks; + int err, offset; - err = fat_bmap(inode, iblock, &phys); + err = fat_bmap(inode, iblock, &phys, &mapped_blocks); if (err) return err; if (phys) { map_bh(bh_result, sb, phys); + *max_blocks = min(mapped_blocks, *max_blocks); return 0; } if (!create) return 0; + if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) { fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)", MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private); return -EIO; } - if (!((unsigned long)iblock & (MSDOS_SB(sb)->sec_per_clus - 1))) { + + offset = (unsigned long)iblock & (sbi->sec_per_clus - 1); + if (!offset) { + /* TODO: multiple cluster allocation would be desirable. */ err = fat_add_cluster(inode); if (err) return err; } - MSDOS_I(inode)->mmu_private += sb->s_blocksize; - err = fat_bmap(inode, iblock, &phys); + /* available blocks on this cluster */ + mapped_blocks = sbi->sec_per_clus - offset; + + *max_blocks = min(mapped_blocks, *max_blocks); + MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits; + + err = fat_bmap(inode, iblock, &phys, &mapped_blocks); if (err) return err; - if (!phys) - BUG(); + BUG_ON(!phys); + BUG_ON(*max_blocks != mapped_blocks); set_buffer_new(bh_result); map_bh(bh_result, sb, phys); return 0; } +static int fat_get_blocks(struct inode *inode, sector_t iblock, + unsigned long max_blocks, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + int err; + + err = __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create); + if (err) + return err; + bh_result->b_size = max_blocks << sb->s_blocksize_bits; + return 0; +} + +static int fat_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + unsigned long max_blocks = 1; + return __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create); +} + static int fat_writepage(struct page *page, struct writeback_control *wbc) { return block_write_full_page(page, fat_get_block, wbc); } +static int fat_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + return mpage_writepages(mapping, wbc, fat_get_block); +} + static int fat_readpage(struct file *file, struct page *page) { - return block_read_full_page(page, fat_get_block); + return mpage_readpage(page, fat_get_block); +} + +static int fat_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + return mpage_readpages(mapping, pages, nr_pages, fat_get_block); } static int fat_prepare_write(struct file *file, struct page *page, @@ -115,6 +163,34 @@ static int fat_commit_write(struct file *file, struct page *page, return err; } +static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, + loff_t offset, unsigned long nr_segs) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_mapping->host; + + if (rw == WRITE) { + /* + * FIXME: blockdev_direct_IO() doesn't use ->prepare_write(), + * so we need to update the ->mmu_private to block boundary. + * + * But we must fill the remaining area or hole by nul for + * updating ->mmu_private. + */ + loff_t size = offset + iov_length(iov, nr_segs); + if (MSDOS_I(inode)->mmu_private < size) + return -EINVAL; + } + + /* + * FAT need to use the DIO_LOCKING for avoiding the race + * condition of fat_get_block() and ->truncate(). + */ + return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + offset, nr_segs, fat_get_blocks, NULL); +} + static sector_t _fat_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping, block, fat_get_block); @@ -122,10 +198,13 @@ static sector_t _fat_bmap(struct address_space *mapping, sector_t block) static struct address_space_operations fat_aops = { .readpage = fat_readpage, + .readpages = fat_readpages, .writepage = fat_writepage, + .writepages = fat_writepages, .sync_page = block_sync_page, .prepare_write = fat_prepare_write, .commit_write = fat_commit_write, + .direct_IO = fat_direct_IO, .bmap = _fat_bmap }; @@ -182,7 +261,7 @@ void fat_attach(struct inode *inode, loff_t i_pos) spin_unlock(&sbi->inode_hash_lock); } -EXPORT_SYMBOL(fat_attach); +EXPORT_SYMBOL_GPL(fat_attach); void fat_detach(struct inode *inode) { @@ -193,7 +272,7 @@ void fat_detach(struct inode *inode) spin_unlock(&sbi->inode_hash_lock); } -EXPORT_SYMBOL(fat_detach); +EXPORT_SYMBOL_GPL(fat_detach); struct inode *fat_iget(struct super_block *sb, loff_t i_pos) { @@ -347,7 +426,7 @@ out: return inode; } -EXPORT_SYMBOL(fat_build_inode); +EXPORT_SYMBOL_GPL(fat_build_inode); static void fat_delete_inode(struct inode *inode) { @@ -374,12 +453,17 @@ static void fat_clear_inode(struct inode *inode) unlock_kernel(); } -static void fat_put_super(struct super_block *sb) +static void fat_write_super(struct super_block *sb) { - struct msdos_sb_info *sbi = MSDOS_SB(sb); + sb->s_dirt = 0; if (!(sb->s_flags & MS_RDONLY)) fat_clusters_flush(sb); +} + +static void fat_put_super(struct super_block *sb) +{ + struct msdos_sb_info *sbi = MSDOS_SB(sb); if (sbi->nls_disk) { unload_nls(sbi->nls_disk); @@ -537,7 +621,7 @@ int fat_sync_inode(struct inode *inode) return fat_write_inode(inode, 1); } -EXPORT_SYMBOL(fat_sync_inode); +EXPORT_SYMBOL_GPL(fat_sync_inode); static int fat_show_options(struct seq_file *m, struct vfsmount *mnt); static struct super_operations fat_sops = { @@ -546,6 +630,7 @@ static struct super_operations fat_sops = { .write_inode = fat_write_inode, .delete_inode = fat_delete_inode, .put_super = fat_put_super, + .write_super = fat_write_super, .statfs = fat_statfs, .clear_inode = fat_clear_inode, .remount_fs = fat_remount, @@ -1347,7 +1432,7 @@ out_fail: return error; } -EXPORT_SYMBOL(fat_fill_super); +EXPORT_SYMBOL_GPL(fat_fill_super); int __init fat_cache_init(void); void fat_cache_destroy(void); diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 2a0df21..32fb0a3 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -33,7 +33,7 @@ void fat_fs_panic(struct super_block *s, const char *fmt, ...) } } -EXPORT_SYMBOL(fat_fs_panic); +EXPORT_SYMBOL_GPL(fat_fs_panic); /* Flushes the number of free clusters on FAT32 */ /* XXX: Need to write one per FSINFO block. Currently only writes 1 */ @@ -67,8 +67,6 @@ void fat_clusters_flush(struct super_block *sb) if (sbi->prev_free != -1) fsinfo->next_cluster = cpu_to_le32(sbi->prev_free); mark_buffer_dirty(bh); - if (sb->s_flags & MS_SYNCHRONOUS) - sync_dirty_buffer(bh); } brelse(bh); } @@ -194,7 +192,7 @@ void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date) *date = cpu_to_le16(nl_day-day_n[month-1]+1+(month << 5)+(year << 9)); } -EXPORT_SYMBOL(fat_date_unix2dos); +EXPORT_SYMBOL_GPL(fat_date_unix2dos); int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) { @@ -222,4 +220,4 @@ int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) return err; } -EXPORT_SYMBOL(fat_sync_bhs); +EXPORT_SYMBOL_GPL(fat_sync_bhs); @@ -457,11 +457,11 @@ static void send_sigio_to_task(struct task_struct *p, else si.si_band = band_table[reason - POLL_IN]; si.si_fd = fd; - if (!send_group_sig_info(fown->signum, &si, p)) + if (!group_send_sig_info(fown->signum, &si, p)) break; /* fall-through: fall back on the old plain SIGIO signal */ case 0: - send_group_sig_info(SIGIO, SEND_SIG_PRIV, p); + group_send_sig_info(SIGIO, SEND_SIG_PRIV, p); } } @@ -495,7 +495,7 @@ static void send_sigurg_to_task(struct task_struct *p, struct fown_struct *fown) { if (sigio_perm(p, fown, SIGURG)) - send_group_sig_info(SIGURG, SEND_SIG_PRIV, p); + group_send_sig_info(SIGURG, SEND_SIG_PRIV, p); } int send_sigurg(struct fown_struct *fown) diff --git a/fs/file_table.c b/fs/file_table.c index c3a5e2f..6142250 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -117,7 +117,7 @@ EXPORT_SYMBOL(get_empty_filp); void fastcall fput(struct file *file) { - if (rcuref_dec_and_test(&file->f_count)) + if (atomic_dec_and_test(&file->f_count)) __fput(file); } @@ -166,7 +166,7 @@ struct file fastcall *fget(unsigned int fd) rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - if (!rcuref_inc_lf(&file->f_count)) { + if (!atomic_inc_not_zero(&file->f_count)) { /* File object ref couldn't be taken */ rcu_read_unlock(); return NULL; @@ -198,7 +198,7 @@ struct file fastcall *fget_light(unsigned int fd, int *fput_needed) rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - if (rcuref_inc_lf(&file->f_count)) + if (atomic_inc_not_zero(&file->f_count)) *fput_needed = 1; else /* Didn't get the reference, someone's freed */ @@ -213,7 +213,7 @@ struct file fastcall *fget_light(unsigned int fd, int *fput_needed) void put_filp(struct file *file) { - if (rcuref_dec_and_test(&file->f_count)) { + if (atomic_dec_and_test(&file->f_count)) { security_file_free(file); file_kill(file); file_free(file); diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c index d0401dc6..6f5df17 100644 --- a/fs/freevxfs/vxfs_immed.c +++ b/fs/freevxfs/vxfs_immed.c @@ -99,8 +99,8 @@ static int vxfs_immed_readpage(struct file *fp, struct page *pp) { struct vxfs_inode_info *vip = VXFS_INO(pp->mapping->host); - u_int64_t offset = pp->index << PAGE_CACHE_SHIFT; - caddr_t kaddr; + u_int64_t offset = (u_int64_t)pp->index << PAGE_CACHE_SHIFT; + caddr_t kaddr; kaddr = kmap(pp); memcpy(kaddr, vip->vii_immed.vi_immed + offset, PAGE_CACHE_SIZE); @@ -770,7 +770,7 @@ EXPORT_SYMBOL(igrab); * * Note, @test is called with the inode_lock held, so can't sleep. */ -static inline struct inode *ifind(struct super_block *sb, +static struct inode *ifind(struct super_block *sb, struct hlist_head *head, int (*test)(struct inode *, void *), void *data, const int wait) { @@ -804,7 +804,7 @@ static inline struct inode *ifind(struct super_block *sb, * * Otherwise NULL is returned. */ -static inline struct inode *ifind_fast(struct super_block *sb, +static struct inode *ifind_fast(struct super_block *sb, struct hlist_head *head, unsigned long ino) { struct inode *inode; diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 3dcc6d2..2559ee1 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -757,7 +757,7 @@ jffs_do_readpage_nolock(struct file *file, struct page *page) read_len = 0; result = 0; - offset = page->index << PAGE_CACHE_SHIFT; + offset = page_offset(page); kmap(page); buf = page_address(page); @@ -1545,7 +1545,7 @@ jffs_commit_write(struct file *filp, struct page *page, { void *addr = page_address(page) + from; /* XXX: PAGE_CACHE_SHIFT or PAGE_SHIFT */ - loff_t pos = (page->index<<PAGE_CACHE_SHIFT) + from; + loff_t pos = page_offset(page) + from; return jffs_file_write(filp, addr, to-from, &pos); } /* jffs_commit_write() */ diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c index 68000a5..2967b73 100644 --- a/fs/jfs/jfs_dmap.c +++ b/fs/jfs/jfs_dmap.c @@ -302,8 +302,7 @@ int dbSync(struct inode *ipbmap) /* * write out dirty pages of bmap */ - filemap_fdatawrite(ipbmap->i_mapping); - filemap_fdatawait(ipbmap->i_mapping); + filemap_write_and_wait(ipbmap->i_mapping); diWriteSpecial(ipbmap, 0); diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c index 28201b1..31b4aa1 100644 --- a/fs/jfs/jfs_imap.c +++ b/fs/jfs/jfs_imap.c @@ -265,8 +265,7 @@ int diSync(struct inode *ipimap) /* * write out dirty pages of imap */ - filemap_fdatawrite(ipimap->i_mapping); - filemap_fdatawait(ipimap->i_mapping); + filemap_write_and_wait(ipimap->i_mapping); diWriteSpecial(ipimap, 0); @@ -565,8 +564,7 @@ void diFreeSpecial(struct inode *ip) jfs_err("diFreeSpecial called with NULL ip!"); return; } - filemap_fdatawrite(ip->i_mapping); - filemap_fdatawait(ip->i_mapping); + filemap_write_and_wait(ip->i_mapping); truncate_inode_pages(ip->i_mapping, 0); iput(ip); } diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c index b660c93..2ddb6b8 100644 --- a/fs/jfs/jfs_txnmgr.c +++ b/fs/jfs/jfs_txnmgr.c @@ -1231,10 +1231,8 @@ int txCommit(tid_t tid, /* transaction identifier */ * when we don't need to worry about it at all. * * if ((!S_ISDIR(ip->i_mode)) - * && (tblk->flag & COMMIT_DELETE) == 0) { - * filemap_fdatawrite(ip->i_mapping); - * filemap_fdatawait(ip->i_mapping); - * } + * && (tblk->flag & COMMIT_DELETE) == 0) + * filemap_write_and_wait(ip->i_mapping); */ /* diff --git a/fs/jfs/jfs_umount.c b/fs/jfs/jfs_umount.c index 5cf9178..21eaf7a 100644 --- a/fs/jfs/jfs_umount.c +++ b/fs/jfs/jfs_umount.c @@ -108,8 +108,7 @@ int jfs_umount(struct super_block *sb) * Make sure all metadata makes it to disk before we mark * the superblock as clean */ - filemap_fdatawrite(sbi->direct_inode->i_mapping); - filemap_fdatawait(sbi->direct_inode->i_mapping); + filemap_write_and_wait(sbi->direct_inode->i_mapping); /* * ensure all file system file pages are propagated to their @@ -161,8 +160,7 @@ int jfs_umount_rw(struct super_block *sb) * mark the superblock clean before everything is flushed to * disk. */ - filemap_fdatawrite(sbi->direct_inode->i_mapping); - filemap_fdatawait(sbi->direct_inode->i_mapping); + filemap_write_and_wait(sbi->direct_inode->i_mapping); updateSuper(sb, FM_CLEAN); diff --git a/fs/jfs/resize.c b/fs/jfs/resize.c index c6dc254..4518036 100644 --- a/fs/jfs/resize.c +++ b/fs/jfs/resize.c @@ -376,8 +376,7 @@ int jfs_extendfs(struct super_block *sb, s64 newLVSize, int newLogSize) * by txCommit(); */ filemap_fdatawait(ipbmap->i_mapping); - filemap_fdatawrite(ipbmap->i_mapping); - filemap_fdatawait(ipbmap->i_mapping); + filemap_write_and_wait(ipbmap->i_mapping); diWriteSpecial(ipbmap, 0); newPage = nPages; /* first new page number */ diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 4226af3..8d31f13 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -502,8 +502,7 @@ out_no_rw: jfs_err("jfs_umount failed with return code %d", rc); } out_mount_failed: - filemap_fdatawrite(sbi->direct_inode->i_mapping); - filemap_fdatawait(sbi->direct_inode->i_mapping); + filemap_write_and_wait(sbi->direct_inode->i_mapping); truncate_inode_pages(sbi->direct_inode->i_mapping, 0); make_bad_inode(sbi->direct_inode); iput(sbi->direct_inode); @@ -93,16 +93,16 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) loff_t n = file->f_pos - 2; spin_lock(&dcache_lock); - list_del(&cursor->d_child); + list_del(&cursor->d_u.d_child); p = file->f_dentry->d_subdirs.next; while (n && p != &file->f_dentry->d_subdirs) { struct dentry *next; - next = list_entry(p, struct dentry, d_child); + next = list_entry(p, struct dentry, d_u.d_child); if (!d_unhashed(next) && next->d_inode) n--; p = p->next; } - list_add_tail(&cursor->d_child, p); + list_add_tail(&cursor->d_u.d_child, p); spin_unlock(&dcache_lock); } } @@ -126,7 +126,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) { struct dentry *dentry = filp->f_dentry; struct dentry *cursor = filp->private_data; - struct list_head *p, *q = &cursor->d_child; + struct list_head *p, *q = &cursor->d_u.d_child; ino_t ino; int i = filp->f_pos; @@ -153,7 +153,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) } for (p=q->next; p != &dentry->d_subdirs; p=p->next) { struct dentry *next; - next = list_entry(p, struct dentry, d_child); + next = list_entry(p, struct dentry, d_u.d_child); if (d_unhashed(next) || !next->d_inode) continue; @@ -261,7 +261,7 @@ int simple_empty(struct dentry *dentry) int ret = 0; spin_lock(&dcache_lock); - list_for_each_entry(child, &dentry->d_subdirs, d_child) + list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) if (simple_positive(child)) goto out; ret = 1; @@ -154,7 +154,7 @@ static struct file_lock *locks_alloc_lock(void) } /* Free a lock which is not in use. */ -static inline void locks_free_lock(struct file_lock *fl) +static void locks_free_lock(struct file_lock *fl) { if (fl == NULL) { BUG(); @@ -475,8 +475,7 @@ static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2) /* * Check whether two locks have the same owner. */ -static inline int -posix_same_owner(struct file_lock *fl1, struct file_lock *fl2) +static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2) { if (fl1->fl_lmops && fl1->fl_lmops->fl_compare_owner) return fl2->fl_lmops == fl1->fl_lmops && @@ -487,7 +486,7 @@ posix_same_owner(struct file_lock *fl1, struct file_lock *fl2) /* Remove waiter from blocker's block list. * When blocker ends up pointing to itself then the list is empty. */ -static inline void __locks_delete_block(struct file_lock *waiter) +static void __locks_delete_block(struct file_lock *waiter) { list_del_init(&waiter->fl_block); list_del_init(&waiter->fl_link); @@ -184,7 +184,7 @@ do_mpage_readpage(struct bio *bio, struct page *page, unsigned nr_pages, if (page_has_buffers(page)) goto confused; - block_in_file = page->index << (PAGE_CACHE_SHIFT - blkbits); + block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits); last_block = (i_size_read(inode) + blocksize - 1) >> blkbits; bh.b_page = page; @@ -466,7 +466,7 @@ __mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, * The page has no buffers: map it to disk */ BUG_ON(!PageUptodate(page)); - block_in_file = page->index << (PAGE_CACHE_SHIFT - blkbits); + block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits); last_block = (i_size - 1) >> blkbits; map_bh.b_page = page; for (page_block = 0; page_block < blocks_per_page; ) { @@ -1491,7 +1491,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) if (!error) { DQUOT_INIT(inode); - error = do_truncate(dentry, 0, NULL); + error = do_truncate(dentry, 0, ATTR_MTIME|ATTR_CTIME, NULL); } put_write_access(inode); if (error) diff --git a/fs/namespace.c b/fs/namespace.c index 2019899f..3e8fb61 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -451,7 +451,7 @@ EXPORT_SYMBOL(may_umount); void release_mounts(struct list_head *head) { struct vfsmount *mnt; - while(!list_empty(head)) { + while (!list_empty(head)) { mnt = list_entry(head->next, struct vfsmount, mnt_hash); list_del_init(&mnt->mnt_hash); if (mnt->mnt_parent != mnt) { @@ -1526,6 +1526,10 @@ static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd) * pointed to by put_old must yield the same directory as new_root. No other * file system may be mounted on put_old. After all, new_root is a mountpoint. * + * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem. + * See Documentation/filesystems/ramfs-rootfs-initramfs.txt for alternatives + * in this situation. + * * Notes: * - we don't move root/cwd if they are not at the root (reason: if something * cared enough to change them, it's probably wrong to force them elsewhere) diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index a9f7a8a..cfd76f4 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -365,7 +365,7 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { - dent = list_entry(next, struct dentry, d_child); + dent = list_entry(next, struct dentry, d_u.d_child); if ((unsigned long)dent->d_fsdata == fpos) { if (dent->d_inode) dget_locked(dent); diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index 9e4dc30..799e5c2 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -196,7 +196,7 @@ ncp_renew_dentries(struct dentry *parent) spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { - dentry = list_entry(next, struct dentry, d_child); + dentry = list_entry(next, struct dentry, d_u.d_child); if (dentry->d_fsdata == NULL) ncp_age_dentry(server, dentry); @@ -218,7 +218,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent) spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { - dentry = list_entry(next, struct dentry, d_child); + dentry = list_entry(next, struct dentry, d_u.d_child); dentry->d_fsdata = NULL; ncp_age_dentry(server, dentry); next = next->next; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index e7bd0d9..3e4ba9c 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -644,10 +644,7 @@ int nfs_sync_mapping(struct address_space *mapping) if (mapping->nrpages == 0) return 0; unmap_mapping_range(mapping, 0, 0, 0); - ret = filemap_fdatawrite(mapping); - if (ret != 0) - goto out; - ret = filemap_fdatawait(mapping); + ret = filemap_write_and_wait(mapping); if (ret != 0) goto out; ret = nfs_wb_all(mapping->host); @@ -864,8 +861,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) nfs_begin_data_update(inode); /* Write all dirty data if we're changing file permissions or size */ if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE)) != 0) { - if (filemap_fdatawrite(inode->i_mapping) == 0) - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); nfs_wb_all(inode); } /* diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 985cc53..e897e00 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -275,7 +275,9 @@ static int __init root_nfs_parse(char *name, char *buf) case Opt_noacl: nfs_data.flags |= NFS_MOUNT_NOACL; break; - default : + default: + printk(KERN_WARNING "Root-NFS: unknown " + "option: %s\n", p); return 0; } } diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h index f5ef5ea..e8c56a3 100644 --- a/fs/ocfs2/cluster/masklog.h +++ b/fs/ocfs2/cluster/masklog.h @@ -212,11 +212,10 @@ extern struct mlog_bits mlog_and_bits, mlog_not_bits; mlog(ML_ENTRY, "ENTRY:\n"); \ } while (0) -/* We disable this for old compilers since they don't have support for - * __builtin_types_compatible_p. +/* + * We disable this for sparse. */ -#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) && \ - !defined(__CHECKER__) +#if !defined(__CHECKER__) #define mlog_exit(st) do { \ if (__builtin_types_compatible_p(typeof(st), unsigned long)) \ mlog(ML_EXIT, "EXIT: %lu\n", (unsigned long) (st)); \ @@ -194,7 +194,8 @@ out: return error; } -int do_truncate(struct dentry *dentry, loff_t length, struct file *filp) +int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, + struct file *filp) { int err; struct iattr newattrs; @@ -204,7 +205,7 @@ int do_truncate(struct dentry *dentry, loff_t length, struct file *filp) return -EINVAL; newattrs.ia_size = length; - newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; + newattrs.ia_valid = ATTR_SIZE | time_attrs; if (filp) { newattrs.ia_file = filp; newattrs.ia_valid |= ATTR_FILE; @@ -216,7 +217,7 @@ int do_truncate(struct dentry *dentry, loff_t length, struct file *filp) return err; } -static inline long do_sys_truncate(const char __user * path, loff_t length) +static long do_sys_truncate(const char __user * path, loff_t length) { struct nameidata nd; struct inode * inode; @@ -266,7 +267,7 @@ static inline long do_sys_truncate(const char __user * path, loff_t length) error = locks_verify_truncate(inode, NULL, length); if (!error) { DQUOT_INIT(inode); - error = do_truncate(nd.dentry, length, NULL); + error = do_truncate(nd.dentry, length, 0, NULL); } put_write_access(inode); @@ -282,7 +283,7 @@ asmlinkage long sys_truncate(const char __user * path, unsigned long length) return do_sys_truncate(path, (long)length); } -static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small) +static long do_sys_ftruncate(unsigned int fd, loff_t length, int small) { struct inode * inode; struct dentry *dentry; @@ -318,7 +319,7 @@ static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small) error = locks_verify_truncate(inode, file, length); if (!error) - error = do_truncate(dentry, length, file); + error = do_truncate(dentry, length, 0, file); out_putf: fput(file); out: @@ -970,7 +971,7 @@ out: EXPORT_SYMBOL(get_unused_fd); -static inline void __put_unused_fd(struct files_struct *files, unsigned int fd) +static void __put_unused_fd(struct files_struct *files, unsigned int fd) { struct fdtable *fdt = files_fdtable(files); __FD_CLR(fd, fdt->open_fds); @@ -103,7 +103,7 @@ static struct vfsmount *propagation_next(struct vfsmount *m, struct vfsmount *next; struct vfsmount *master = m->mnt_master; - if ( master == origin->mnt_master ) { + if (master == origin->mnt_master) { next = next_peer(m); return ((next == origin) ? NULL : next); } else if (m->mnt_slave.next != &master->mnt_slave_list) diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 72b431d..20e5c45 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -21,6 +21,8 @@ #include <linux/bitops.h> #include <asm/uaccess.h> +#include "internal.h" + static ssize_t proc_file_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos); static ssize_t proc_file_write(struct file *file, const char __user *buffer, diff --git a/fs/proc/inode.c b/fs/proc/inode.c index e6a818a..6573f31 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -19,7 +19,7 @@ #include <asm/system.h> #include <asm/uaccess.h> -extern void free_proc_entry(struct proc_dir_entry *); +#include "internal.h" static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de) { diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 3e55198..95a1cf3 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -37,6 +37,10 @@ extern int proc_tgid_stat(struct task_struct *, char *); extern int proc_pid_status(struct task_struct *, char *); extern int proc_pid_statm(struct task_struct *, char *); +void free_proc_entry(struct proc_dir_entry *de); + +int proc_init_inodecache(void); + static inline struct task_struct *proc_task(struct inode *inode) { return PROC_I(inode)->task; diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 5b6b0b6..63bf6c0 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -323,6 +323,7 @@ static struct file_operations proc_modules_operations = { }; #endif +#ifdef CONFIG_SLAB extern struct seq_operations slabinfo_op; extern ssize_t slabinfo_write(struct file *, const char __user *, size_t, loff_t *); static int slabinfo_open(struct inode *inode, struct file *file) @@ -336,6 +337,7 @@ static struct file_operations proc_slabinfo_operations = { .llseek = seq_lseek, .release = seq_release, }; +#endif static int show_stat(struct seq_file *p, void *v) { @@ -600,7 +602,9 @@ void __init proc_misc_init(void) create_seq_entry("partitions", 0, &proc_partitions_operations); create_seq_entry("stat", 0, &proc_stat_operations); create_seq_entry("interrupts", 0, &proc_interrupts_operations); +#ifdef CONFIG_SLAB create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations); +#endif create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations); create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations); create_seq_entry("zoneinfo",S_IRUGO, &proc_zoneinfo_file_operations); diff --git a/fs/proc/root.c b/fs/proc/root.c index aef148f..6889628 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -18,6 +18,8 @@ #include <linux/bitops.h> #include <linux/smp_lock.h> +#include "internal.h" + struct proc_dir_entry *proc_net, *proc_net_stat, *proc_bus, *proc_root_fs, *proc_root_driver; #ifdef CONFIG_SYSCTL @@ -36,7 +38,6 @@ static struct file_system_type proc_fs_type = { .kill_sb = kill_anon_super, }; -extern int __init proc_init_inodecache(void); void __init proc_root_init(void) { int err = proc_init_inodecache(); diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 50bd5a8f..0eaad41 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -390,129 +390,12 @@ struct seq_operations proc_pid_smaps_op = { }; #ifdef CONFIG_NUMA - -struct numa_maps { - unsigned long pages; - unsigned long anon; - unsigned long mapped; - unsigned long mapcount_max; - unsigned long node[MAX_NUMNODES]; -}; - -/* - * Calculate numa node maps for a vma - */ -static struct numa_maps *get_numa_maps(struct vm_area_struct *vma) -{ - int i; - struct page *page; - unsigned long vaddr; - struct numa_maps *md = kmalloc(sizeof(struct numa_maps), GFP_KERNEL); - - if (!md) - return NULL; - md->pages = 0; - md->anon = 0; - md->mapped = 0; - md->mapcount_max = 0; - for_each_node(i) - md->node[i] =0; - - for (vaddr = vma->vm_start; vaddr < vma->vm_end; vaddr += PAGE_SIZE) { - page = follow_page(vma, vaddr, 0); - if (page) { - int count = page_mapcount(page); - - if (count) - md->mapped++; - if (count > md->mapcount_max) - md->mapcount_max = count; - md->pages++; - if (PageAnon(page)) - md->anon++; - md->node[page_to_nid(page)]++; - } - cond_resched(); - } - return md; -} - -static int show_numa_map(struct seq_file *m, void *v) -{ - struct task_struct *task = m->private; - struct vm_area_struct *vma = v; - struct mempolicy *pol; - struct numa_maps *md; - struct zone **z; - int n; - int first; - - if (!vma->vm_mm) - return 0; - - md = get_numa_maps(vma); - if (!md) - return 0; - - seq_printf(m, "%08lx", vma->vm_start); - pol = get_vma_policy(task, vma, vma->vm_start); - /* Print policy */ - switch (pol->policy) { - case MPOL_PREFERRED: - seq_printf(m, " prefer=%d", pol->v.preferred_node); - break; - case MPOL_BIND: - seq_printf(m, " bind={"); - first = 1; - for (z = pol->v.zonelist->zones; *z; z++) { - - if (!first) - seq_putc(m, ','); - else - first = 0; - seq_printf(m, "%d/%s", (*z)->zone_pgdat->node_id, - (*z)->name); - } - seq_putc(m, '}'); - break; - case MPOL_INTERLEAVE: - seq_printf(m, " interleave={"); - first = 1; - for_each_node(n) { - if (node_isset(n, pol->v.nodes)) { - if (!first) - seq_putc(m,','); - else - first = 0; - seq_printf(m, "%d",n); - } - } - seq_putc(m, '}'); - break; - default: - seq_printf(m," default"); - break; - } - seq_printf(m, " MaxRef=%lu Pages=%lu Mapped=%lu", - md->mapcount_max, md->pages, md->mapped); - if (md->anon) - seq_printf(m," Anon=%lu",md->anon); - - for_each_online_node(n) { - if (md->node[n]) - seq_printf(m, " N%d=%lu", n, md->node[n]); - } - seq_putc(m, '\n'); - kfree(md); - if (m->count < m->size) /* vma is copied successfully */ - m->version = (vma != get_gate_vma(task)) ? vma->vm_start : 0; - return 0; -} +extern int show_numa_map(struct seq_file *m, void *v); struct seq_operations proc_pid_numa_maps_op = { - .start = m_start, - .next = m_next, - .stop = m_stop, - .show = show_numa_map + .start = m_start, + .next = m_next, + .stop = m_stop, + .show = show_numa_map }; #endif diff --git a/fs/relayfs/buffers.c b/fs/relayfs/buffers.c index 84e21ff..1018781 100644 --- a/fs/relayfs/buffers.c +++ b/fs/relayfs/buffers.c @@ -185,5 +185,6 @@ void relay_destroy_buf(struct rchan_buf *buf) void relay_remove_buf(struct kref *kref) { struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref); - relayfs_remove(buf->dentry); + buf->chan->cb->remove_buf_file(buf->dentry); + relay_destroy_buf(buf); } diff --git a/fs/relayfs/inode.c b/fs/relayfs/inode.c index 0f7f88d..7b7f2cb 100644 --- a/fs/relayfs/inode.c +++ b/fs/relayfs/inode.c @@ -26,31 +26,22 @@ static struct vfsmount * relayfs_mount; static int relayfs_mount_count; -static kmem_cache_t * relayfs_inode_cachep; static struct backing_dev_info relayfs_backing_dev_info = { .ra_pages = 0, /* No readahead */ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, }; -static struct inode *relayfs_get_inode(struct super_block *sb, int mode, - struct rchan *chan) +static struct inode *relayfs_get_inode(struct super_block *sb, + int mode, + struct file_operations *fops, + void *data) { - struct rchan_buf *buf = NULL; struct inode *inode; - if (S_ISREG(mode)) { - BUG_ON(!chan); - buf = relay_create_buf(chan); - if (!buf) - return NULL; - } - inode = new_inode(sb); - if (!inode) { - relay_destroy_buf(buf); + if (!inode) return NULL; - } inode->i_mode = mode; inode->i_uid = 0; @@ -61,8 +52,9 @@ static struct inode *relayfs_get_inode(struct super_block *sb, int mode, inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { case S_IFREG: - inode->i_fop = &relayfs_file_operations; - RELAYFS_I(inode)->buf = buf; + inode->i_fop = fops; + if (data) + inode->u.generic_ip = data; break; case S_IFDIR: inode->i_op = &simple_dir_inode_operations; @@ -83,7 +75,8 @@ static struct inode *relayfs_get_inode(struct super_block *sb, int mode, * @name: the name of the file to create * @parent: parent directory * @mode: mode - * @chan: relay channel associated with the file + * @fops: file operations to use for the file + * @data: user-associated data for this file * * Returns the new dentry, NULL on failure * @@ -92,7 +85,8 @@ static struct inode *relayfs_get_inode(struct super_block *sb, int mode, static struct dentry *relayfs_create_entry(const char *name, struct dentry *parent, int mode, - struct rchan *chan) + struct file_operations *fops, + void *data) { struct dentry *d; struct inode *inode; @@ -127,7 +121,7 @@ static struct dentry *relayfs_create_entry(const char *name, goto release_mount; } - inode = relayfs_get_inode(parent->d_inode->i_sb, mode, chan); + inode = relayfs_get_inode(parent->d_inode->i_sb, mode, fops, data); if (!inode) { d = NULL; goto release_mount; @@ -155,20 +149,26 @@ exit: * @name: the name of the file to create * @parent: parent directory * @mode: mode, if not specied the default perms are used - * @chan: channel associated with the file + * @fops: file operations to use for the file + * @data: user-associated data for this file * * Returns file dentry if successful, NULL otherwise. * * The file will be created user r on behalf of current user. */ -struct dentry *relayfs_create_file(const char *name, struct dentry *parent, - int mode, struct rchan *chan) +struct dentry *relayfs_create_file(const char *name, + struct dentry *parent, + int mode, + struct file_operations *fops, + void *data) { + BUG_ON(!fops); + if (!mode) mode = S_IRUSR; mode = (mode & S_IALLUGO) | S_IFREG; - return relayfs_create_entry(name, parent, mode, chan); + return relayfs_create_entry(name, parent, mode, fops, data); } /** @@ -183,7 +183,7 @@ struct dentry *relayfs_create_file(const char *name, struct dentry *parent, struct dentry *relayfs_create_dir(const char *name, struct dentry *parent) { int mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; - return relayfs_create_entry(name, parent, mode, NULL); + return relayfs_create_entry(name, parent, mode, NULL, NULL); } /** @@ -225,6 +225,17 @@ int relayfs_remove(struct dentry *dentry) } /** + * relayfs_remove_file - remove a file from relay filesystem + * @dentry: directory dentry + * + * Returns 0 if successful, negative otherwise. + */ +int relayfs_remove_file(struct dentry *dentry) +{ + return relayfs_remove(dentry); +} + +/** * relayfs_remove_dir - remove a directory in the relay filesystem * @dentry: directory dentry * @@ -236,45 +247,45 @@ int relayfs_remove_dir(struct dentry *dentry) } /** - * relayfs_open - open file op for relayfs files + * relay_file_open - open file op for relay files * @inode: the inode * @filp: the file * * Increments the channel buffer refcount. */ -static int relayfs_open(struct inode *inode, struct file *filp) +static int relay_file_open(struct inode *inode, struct file *filp) { - struct rchan_buf *buf = RELAYFS_I(inode)->buf; + struct rchan_buf *buf = inode->u.generic_ip; kref_get(&buf->kref); + filp->private_data = buf; return 0; } /** - * relayfs_mmap - mmap file op for relayfs files + * relay_file_mmap - mmap file op for relay files * @filp: the file * @vma: the vma describing what to map * * Calls upon relay_mmap_buf to map the file into user space. */ -static int relayfs_mmap(struct file *filp, struct vm_area_struct *vma) +static int relay_file_mmap(struct file *filp, struct vm_area_struct *vma) { - struct inode *inode = filp->f_dentry->d_inode; - return relay_mmap_buf(RELAYFS_I(inode)->buf, vma); + struct rchan_buf *buf = filp->private_data; + return relay_mmap_buf(buf, vma); } /** - * relayfs_poll - poll file op for relayfs files + * relay_file_poll - poll file op for relay files * @filp: the file * @wait: poll table * * Poll implemention. */ -static unsigned int relayfs_poll(struct file *filp, poll_table *wait) +static unsigned int relay_file_poll(struct file *filp, poll_table *wait) { unsigned int mask = 0; - struct inode *inode = filp->f_dentry->d_inode; - struct rchan_buf *buf = RELAYFS_I(inode)->buf; + struct rchan_buf *buf = filp->private_data; if (buf->finalized) return POLLERR; @@ -289,27 +300,27 @@ static unsigned int relayfs_poll(struct file *filp, poll_table *wait) } /** - * relayfs_release - release file op for relayfs files + * relay_file_release - release file op for relay files * @inode: the inode * @filp: the file * * Decrements the channel refcount, as the filesystem is * no longer using it. */ -static int relayfs_release(struct inode *inode, struct file *filp) +static int relay_file_release(struct inode *inode, struct file *filp) { - struct rchan_buf *buf = RELAYFS_I(inode)->buf; + struct rchan_buf *buf = filp->private_data; kref_put(&buf->kref, relay_remove_buf); return 0; } /** - * relayfs_read_consume - update the consumed count for the buffer + * relay_file_read_consume - update the consumed count for the buffer */ -static void relayfs_read_consume(struct rchan_buf *buf, - size_t read_pos, - size_t bytes_consumed) +static void relay_file_read_consume(struct rchan_buf *buf, + size_t read_pos, + size_t bytes_consumed) { size_t subbuf_size = buf->chan->subbuf_size; size_t n_subbufs = buf->chan->n_subbufs; @@ -332,9 +343,9 @@ static void relayfs_read_consume(struct rchan_buf *buf, } /** - * relayfs_read_avail - boolean, are there unconsumed bytes available? + * relay_file_read_avail - boolean, are there unconsumed bytes available? */ -static int relayfs_read_avail(struct rchan_buf *buf, size_t read_pos) +static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos) { size_t bytes_produced, bytes_consumed, write_offset; size_t subbuf_size = buf->chan->subbuf_size; @@ -365,16 +376,16 @@ static int relayfs_read_avail(struct rchan_buf *buf, size_t read_pos) if (bytes_produced == bytes_consumed) return 0; - relayfs_read_consume(buf, read_pos, 0); + relay_file_read_consume(buf, read_pos, 0); return 1; } /** - * relayfs_read_subbuf_avail - return bytes available in sub-buffer + * relay_file_read_subbuf_avail - return bytes available in sub-buffer */ -static size_t relayfs_read_subbuf_avail(size_t read_pos, - struct rchan_buf *buf) +static size_t relay_file_read_subbuf_avail(size_t read_pos, + struct rchan_buf *buf) { size_t padding, avail = 0; size_t read_subbuf, read_offset, write_subbuf, write_offset; @@ -396,14 +407,14 @@ static size_t relayfs_read_subbuf_avail(size_t read_pos, } /** - * relayfs_read_start_pos - find the first available byte to read + * relay_file_read_start_pos - find the first available byte to read * * If the read_pos is in the middle of padding, return the * position of the first actually available byte, otherwise * return the original value. */ -static size_t relayfs_read_start_pos(size_t read_pos, - struct rchan_buf *buf) +static size_t relay_file_read_start_pos(size_t read_pos, + struct rchan_buf *buf) { size_t read_subbuf, padding, padding_start, padding_end; size_t subbuf_size = buf->chan->subbuf_size; @@ -422,11 +433,11 @@ static size_t relayfs_read_start_pos(size_t read_pos, } /** - * relayfs_read_end_pos - return the new read position + * relay_file_read_end_pos - return the new read position */ -static size_t relayfs_read_end_pos(struct rchan_buf *buf, - size_t read_pos, - size_t count) +static size_t relay_file_read_end_pos(struct rchan_buf *buf, + size_t read_pos, + size_t count) { size_t read_subbuf, padding, end_pos; size_t subbuf_size = buf->chan->subbuf_size; @@ -445,7 +456,7 @@ static size_t relayfs_read_end_pos(struct rchan_buf *buf, } /** - * relayfs_read - read file op for relayfs files + * relay_file_read - read file op for relay files * @filp: the file * @buffer: the userspace buffer * @count: number of bytes to read @@ -454,23 +465,23 @@ static size_t relayfs_read_end_pos(struct rchan_buf *buf, * Reads count bytes or the number of bytes available in the * current sub-buffer being read, whichever is smaller. */ -static ssize_t relayfs_read(struct file *filp, - char __user *buffer, - size_t count, - loff_t *ppos) +static ssize_t relay_file_read(struct file *filp, + char __user *buffer, + size_t count, + loff_t *ppos) { + struct rchan_buf *buf = filp->private_data; struct inode *inode = filp->f_dentry->d_inode; - struct rchan_buf *buf = RELAYFS_I(inode)->buf; size_t read_start, avail; ssize_t ret = 0; void *from; down(&inode->i_sem); - if(!relayfs_read_avail(buf, *ppos)) + if(!relay_file_read_avail(buf, *ppos)) goto out; - read_start = relayfs_read_start_pos(*ppos, buf); - avail = relayfs_read_subbuf_avail(read_start, buf); + read_start = relay_file_read_start_pos(*ppos, buf); + avail = relay_file_read_subbuf_avail(read_start, buf); if (!avail) goto out; @@ -480,58 +491,25 @@ static ssize_t relayfs_read(struct file *filp, ret = -EFAULT; goto out; } - relayfs_read_consume(buf, read_start, count); - *ppos = relayfs_read_end_pos(buf, read_start, count); + relay_file_read_consume(buf, read_start, count); + *ppos = relay_file_read_end_pos(buf, read_start, count); out: up(&inode->i_sem); return ret; } -/** - * relayfs alloc_inode() implementation - */ -static struct inode *relayfs_alloc_inode(struct super_block *sb) -{ - struct relayfs_inode_info *p = kmem_cache_alloc(relayfs_inode_cachep, SLAB_KERNEL); - if (!p) - return NULL; - p->buf = NULL; - - return &p->vfs_inode; -} - -/** - * relayfs destroy_inode() implementation - */ -static void relayfs_destroy_inode(struct inode *inode) -{ - if (RELAYFS_I(inode)->buf) - relay_destroy_buf(RELAYFS_I(inode)->buf); - - kmem_cache_free(relayfs_inode_cachep, RELAYFS_I(inode)); -} - -static void init_once(void *p, kmem_cache_t *cachep, unsigned long flags) -{ - struct relayfs_inode_info *i = p; - if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) - inode_init_once(&i->vfs_inode); -} - -struct file_operations relayfs_file_operations = { - .open = relayfs_open, - .poll = relayfs_poll, - .mmap = relayfs_mmap, - .read = relayfs_read, +struct file_operations relay_file_operations = { + .open = relay_file_open, + .poll = relay_file_poll, + .mmap = relay_file_mmap, + .read = relay_file_read, .llseek = no_llseek, - .release = relayfs_release, + .release = relay_file_release, }; static struct super_operations relayfs_ops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, - .alloc_inode = relayfs_alloc_inode, - .destroy_inode = relayfs_destroy_inode, }; static int relayfs_fill_super(struct super_block * sb, void * data, int silent) @@ -544,7 +522,7 @@ static int relayfs_fill_super(struct super_block * sb, void * data, int silent) sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = RELAYFS_MAGIC; sb->s_op = &relayfs_ops; - inode = relayfs_get_inode(sb, mode, NULL); + inode = relayfs_get_inode(sb, mode, NULL, NULL); if (!inode) return -ENOMEM; @@ -575,33 +553,27 @@ static struct file_system_type relayfs_fs_type = { static int __init init_relayfs_fs(void) { - int err; - - relayfs_inode_cachep = kmem_cache_create("relayfs_inode_cache", - sizeof(struct relayfs_inode_info), 0, - 0, init_once, NULL); - if (!relayfs_inode_cachep) - return -ENOMEM; - - err = register_filesystem(&relayfs_fs_type); - if (err) - kmem_cache_destroy(relayfs_inode_cachep); - - return err; + return register_filesystem(&relayfs_fs_type); } static void __exit exit_relayfs_fs(void) { + + + + + unregister_filesystem(&relayfs_fs_type); - kmem_cache_destroy(relayfs_inode_cachep); } module_init(init_relayfs_fs) module_exit(exit_relayfs_fs) -EXPORT_SYMBOL_GPL(relayfs_file_operations); +EXPORT_SYMBOL_GPL(relay_file_operations); EXPORT_SYMBOL_GPL(relayfs_create_dir); EXPORT_SYMBOL_GPL(relayfs_remove_dir); +EXPORT_SYMBOL_GPL(relayfs_create_file); +EXPORT_SYMBOL_GPL(relayfs_remove_file); MODULE_AUTHOR("Tom Zanussi <zanussi@us.ibm.com> and Karim Yaghmour <karim@opersys.com>"); MODULE_DESCRIPTION("Relay Filesystem"); diff --git a/fs/relayfs/relay.c b/fs/relayfs/relay.c index 2a6f7f1..abf3cea 100644 --- a/fs/relayfs/relay.c +++ b/fs/relayfs/relay.c @@ -80,11 +80,34 @@ static void buf_unmapped_default_callback(struct rchan_buf *buf, { } +/* + * create_buf_file_create() default callback. Creates file to represent buf. + */ +static struct dentry *create_buf_file_default_callback(const char *filename, + struct dentry *parent, + int mode, + struct rchan_buf *buf, + int *is_global) +{ + return relayfs_create_file(filename, parent, mode, + &relay_file_operations, buf); +} + +/* + * remove_buf_file() default callback. Removes file representing relay buffer. + */ +static int remove_buf_file_default_callback(struct dentry *dentry) +{ + return relayfs_remove(dentry); +} + /* relay channel default callbacks */ static struct rchan_callbacks default_channel_callbacks = { .subbuf_start = subbuf_start_default_callback, .buf_mapped = buf_mapped_default_callback, .buf_unmapped = buf_unmapped_default_callback, + .create_buf_file = create_buf_file_default_callback, + .remove_buf_file = remove_buf_file_default_callback, }; /** @@ -148,14 +171,16 @@ static inline void __relay_reset(struct rchan_buf *buf, unsigned int init) void relay_reset(struct rchan *chan) { unsigned int i; + struct rchan_buf *prev = NULL; if (!chan) return; for (i = 0; i < NR_CPUS; i++) { - if (!chan->buf[i]) - continue; + if (!chan->buf[i] || chan->buf[i] == prev) + break; __relay_reset(chan->buf[i], 0); + prev = chan->buf[i]; } } @@ -166,17 +191,27 @@ void relay_reset(struct rchan *chan) */ static struct rchan_buf *relay_open_buf(struct rchan *chan, const char *filename, - struct dentry *parent) + struct dentry *parent, + int *is_global) { struct rchan_buf *buf; struct dentry *dentry; + if (*is_global) + return chan->buf[0]; + + buf = relay_create_buf(chan); + if (!buf) + return NULL; + /* Create file in fs */ - dentry = relayfs_create_file(filename, parent, S_IRUSR, chan); - if (!dentry) + dentry = chan->cb->create_buf_file(filename, parent, S_IRUSR, + buf, is_global); + if (!dentry) { + relay_destroy_buf(buf); return NULL; + } - buf = RELAYFS_I(dentry->d_inode)->buf; buf->dentry = dentry; __relay_reset(buf, 1); @@ -214,6 +249,10 @@ static inline void setup_callbacks(struct rchan *chan, cb->buf_mapped = buf_mapped_default_callback; if (!cb->buf_unmapped) cb->buf_unmapped = buf_unmapped_default_callback; + if (!cb->create_buf_file) + cb->create_buf_file = create_buf_file_default_callback; + if (!cb->remove_buf_file) + cb->remove_buf_file = remove_buf_file_default_callback; chan->cb = cb; } @@ -241,6 +280,7 @@ struct rchan *relay_open(const char *base_filename, unsigned int i; struct rchan *chan; char *tmpname; + int is_global = 0; if (!base_filename) return NULL; @@ -265,7 +305,8 @@ struct rchan *relay_open(const char *base_filename, for_each_online_cpu(i) { sprintf(tmpname, "%s%d", base_filename, i); - chan->buf[i] = relay_open_buf(chan, tmpname, parent); + chan->buf[i] = relay_open_buf(chan, tmpname, parent, + &is_global); chan->buf[i]->cpu = i; if (!chan->buf[i]) goto free_bufs; @@ -279,6 +320,8 @@ free_bufs: if (!chan->buf[i]) break; relay_close_buf(chan->buf[i]); + if (is_global) + break; } kfree(tmpname); @@ -388,14 +431,16 @@ void relay_destroy_channel(struct kref *kref) void relay_close(struct rchan *chan) { unsigned int i; + struct rchan_buf *prev = NULL; if (!chan) return; for (i = 0; i < NR_CPUS; i++) { - if (!chan->buf[i]) - continue; + if (!chan->buf[i] || chan->buf[i] == prev) + break; relay_close_buf(chan->buf[i]); + prev = chan->buf[i]; } if (chan->last_toobig) @@ -415,14 +460,16 @@ void relay_close(struct rchan *chan) void relay_flush(struct rchan *chan) { unsigned int i; + struct rchan_buf *prev = NULL; if (!chan) return; for (i = 0; i < NR_CPUS; i++) { - if (!chan->buf[i]) - continue; + if (!chan->buf[i] || chan->buf[i] == prev) + break; relay_switch_subbuf(chan->buf[i], 0); + prev = chan->buf[i]; } } diff --git a/fs/relayfs/relay.h b/fs/relayfs/relay.h index 703503f..0993d3e 100644 --- a/fs/relayfs/relay.h +++ b/fs/relayfs/relay.h @@ -1,10 +1,6 @@ #ifndef _RELAY_H #define _RELAY_H -struct dentry *relayfs_create_file(const char *name, - struct dentry *parent, - int mode, - struct rchan *chan); extern int relayfs_remove(struct dentry *dentry); extern int relay_buf_empty(struct rchan_buf *buf); extern void relay_destroy_channel(struct kref *kref); diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index c74f382..0a13859 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -418,7 +418,7 @@ static int romfs_readpage(struct file *file, struct page * page) { struct inode *inode = page->mapping->host; - unsigned long offset, avail, readlen; + loff_t offset, avail, readlen; void *buf; int result = -EIO; @@ -429,8 +429,8 @@ romfs_readpage(struct file *file, struct page * page) goto err_out; /* 32 bit warning -- but not for us :) */ - offset = page->index << PAGE_CACHE_SHIFT; - if (offset < inode->i_size) { + offset = page_offset(page); + if (offset < i_size_read(inode)) { avail = inode->i_size-offset; readlen = min_t(unsigned long, avail, PAGE_SIZE); if (romfs_copyfrom(inode, buf, ROMFS_I(inode)->i_dataoffset+offset, readlen) == readlen) { diff --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c index f3e6b81..74b86d9 100644 --- a/fs/smbfs/cache.c +++ b/fs/smbfs/cache.c @@ -66,7 +66,7 @@ smb_invalidate_dircache_entries(struct dentry *parent) spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { - dentry = list_entry(next, struct dentry, d_child); + dentry = list_entry(next, struct dentry, d_u.d_child); dentry->d_fsdata = NULL; smb_age_dentry(server, dentry); next = next->next; @@ -100,7 +100,7 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { - dent = list_entry(next, struct dentry, d_child); + dent = list_entry(next, struct dentry, d_u.d_child); if ((unsigned long)dent->d_fsdata == fpos) { if (dent->d_inode) dget_locked(dent); diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index b4fcfa8..7042e62 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -209,8 +209,8 @@ smb_updatepage(struct file *file, struct page *page, unsigned long offset, { struct dentry *dentry = file->f_dentry; - DEBUG1("(%s/%s %d@%ld)\n", DENTRY_PATH(dentry), - count, (page->index << PAGE_CACHE_SHIFT)+offset); + DEBUG1("(%s/%s %d@%lld)\n", DENTRY_PATH(dentry), count, + ((unsigned long long)page->index << PAGE_CACHE_SHIFT) + offset); return smb_writepage_sync(dentry->d_inode, page, offset, count); } @@ -374,8 +374,7 @@ smb_file_release(struct inode *inode, struct file * file) /* We must flush any dirty pages now as we won't be able to write anything after close. mmap can trigger this. "openers" should perhaps include mmap'ers ... */ - filemap_fdatawrite(inode->i_mapping); - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); smb_close(inode); } unlock_kernel(); diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 10b9944..6ec88bf 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -697,8 +697,7 @@ smb_notify_change(struct dentry *dentry, struct iattr *attr) DENTRY_PATH(dentry), (long) inode->i_size, (long) attr->ia_size); - filemap_fdatawrite(inode->i_mapping); - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); error = smb_open(dentry, O_WRONLY); if (error) diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 38ab558..d6baec0 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -3113,7 +3113,7 @@ smb_proc_setattr_unix(struct dentry *d, struct iattr *attr, LSET(data, 32, SMB_TIME_NO_CHANGE); LSET(data, 40, SMB_UID_NO_CHANGE); LSET(data, 48, SMB_GID_NO_CHANGE); - LSET(data, 56, smb_filetype_from_mode(attr->ia_mode)); + DSET(data, 56, smb_filetype_from_mode(attr->ia_mode)); LSET(data, 60, major); LSET(data, 68, minor); LSET(data, 76, 0); @@ -700,8 +700,7 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type, s->s_flags = flags; strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id)); - s->s_old_blocksize = block_size(bdev); - sb_set_blocksize(s, s->s_old_blocksize); + sb_set_blocksize(s, block_size(bdev)); error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { up_write(&s->s_umount); diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 69a085a..cce8b05 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -103,7 +103,7 @@ static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) offset = (char *)de - kaddr; over = filldir(dirent, name, strnlen(name,SYSV_NAMELEN), - (n<<PAGE_CACHE_SHIFT) | offset, + ((loff_t)n<<PAGE_CACHE_SHIFT) | offset, fs16_to_cpu(SYSV_SB(sb), de->inode), DT_UNKNOWN); if (over) { @@ -115,7 +115,7 @@ static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) } done: - filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; + filp->f_pos = ((loff_t)n << PAGE_CACHE_SHIFT) | offset; unlock_kernel(); return 0; } diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index 6598a50..4fae57d 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -41,7 +41,7 @@ #define uint(x) xuint(x) #define xuint(x) __le ## x -extern inline int find_next_one_bit (void * addr, int size, int offset) +static inline int find_next_one_bit (void * addr, int size, int offset) { uintBPL_t * p = ((uintBPL_t *) addr) + (offset / BITS_PER_LONG); int result = offset & ~(BITS_PER_LONG-1); diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 4014f17..395e582 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1957,11 +1957,6 @@ int8_t inode_bmap(struct inode *inode, int block, kernel_lb_addr *bloc, uint32_t printk(KERN_ERR "udf: inode_bmap: block < 0\n"); return -1; } - if (!inode) - { - printk(KERN_ERR "udf: inode_bmap: NULL inode\n"); - return -1; - } *extoffset = 0; *elen = 0; diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 54828eb..2ba11a9 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1296,8 +1296,10 @@ static ssize_t ufs_quota_write(struct super_block *sb, int type, blk++; } out: - if (len == towrite) + if (len == towrite) { + up(&inode->i_sem); return err; + } if (inode->i_size < off+len-towrite) i_size_write(inode, off+len-towrite); inode->i_version++; diff --git a/fs/xfs/linux-2.6/xfs_fs_subr.c b/fs/xfs/linux-2.6/xfs_fs_subr.c index f89340c..4fa4b1a 100644 --- a/fs/xfs/linux-2.6/xfs_fs_subr.c +++ b/fs/xfs/linux-2.6/xfs_fs_subr.c @@ -79,8 +79,7 @@ fs_flushinval_pages( struct inode *ip = LINVFS_GET_IP(vp); if (VN_CACHED(vp)) { - filemap_fdatawrite(ip->i_mapping); - filemap_fdatawait(ip->i_mapping); + filemap_write_and_wait(ip->i_mapping); truncate_inode_pages(ip->i_mapping, first); } diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index 158829c..f40d439 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -30,13 +30,7 @@ * By comparing each compnent, we don't have to worry about extra * endian issues in treating two 32 bit numbers as one 64 bit number */ -static -#if defined(__GNUC__) && (__GNUC__ == 2) && ( (__GNUC_MINOR__ == 95) || (__GNUC_MINOR__ == 96)) -__attribute__((unused)) /* gcc 2.95, 2.96 miscompile this when inlined */ -#else -__inline__ -#endif -xfs_lsn_t _lsn_cmp(xfs_lsn_t lsn1, xfs_lsn_t lsn2) +static inline xfs_lsn_t _lsn_cmp(xfs_lsn_t lsn1, xfs_lsn_t lsn2) { if (CYCLE_LSN(lsn1) != CYCLE_LSN(lsn2)) return (CYCLE_LSN(lsn1)<CYCLE_LSN(lsn2))? -999 : 999; |