From acaefc25d21f850e47ecc5098d1e0bc442c526be Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 18 May 2005 14:40:59 +0200 Subject: [PATCH] libfs: add simple attribute files Based on the discussion about spufs attributes, this is my suggestion for a more generic attribute file support that can be used by both debugfs and spufs. Simple attribute files behave similarly to sequential files from a kernel programmers perspective in that a standard set of file operations is provided and only an open operation needs to be written that registers file specific get() and set() functions. These operations are defined as void foo_set(void *data, u64 val); and u64 foo_get(void *data); where data is the inode->u.generic_ip pointer of the file and the operations just need to make send of that pointer. The infrastructure makes sure this works correctly with concurrent access and partial read calls. A macro named DEFINE_SIMPLE_ATTRIBUTE is provided to further simplify using the attributes. This patch already contains the changes for debugfs to use attributes for its internal file operations. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- include/linux/fs.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'include/linux/fs.h') diff --git a/include/linux/fs.h b/include/linux/fs.h index 0180102dace1..9b8b696d4f15 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1657,6 +1657,52 @@ static inline void simple_transaction_set(struct file *file, size_t n) ar->size = n; } +/* + * simple attribute files + * + * These attributes behave similar to those in sysfs: + * + * Writing to an attribute immediately sets a value, an open file can be + * written to multiple times. + * + * Reading from an attribute creates a buffer from the value that might get + * read with multiple read calls. When the attribute has been read + * completely, no further read calls are possible until the file is opened + * again. + * + * All attributes contain a text representation of a numeric value + * that are accessed with the get() and set() functions. + */ +#define DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \ +static int __fops ## _open(struct inode *inode, struct file *file) \ +{ \ + __simple_attr_check_format(__fmt, 0ull); \ + return simple_attr_open(inode, file, __get, __set, __fmt); \ +} \ +static struct file_operations __fops = { \ + .owner = THIS_MODULE, \ + .open = __fops ## _open, \ + .release = simple_attr_close, \ + .read = simple_attr_read, \ + .write = simple_attr_write, \ +}; + +static inline void __attribute__((format(printf, 1, 2))) +__simple_attr_check_format(const char *fmt, ...) +{ + /* don't do anything, just let the compiler check the arguments; */ +} + +int simple_attr_open(struct inode *inode, struct file *file, + u64 (*get)(void *), void (*set)(void *, u64), + const char *fmt); +int simple_attr_close(struct inode *inode, struct file *file); +ssize_t simple_attr_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos); +ssize_t simple_attr_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos); + + #ifdef CONFIG_SECURITY static inline char *alloc_secdata(void) { -- cgit v1.2.3 From 8d0a8a9d0ec790086c64d210af413ac351d89e35 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 22 Jun 2005 17:16:32 +0000 Subject: [PATCH] NFSv4: Clean up nfs4 lock state accounting Ensure that lock owner structures are not released prematurely. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 9 +-- fs/nfs/nfs4proc.c | 69 ++++++++---------- fs/nfs/nfs4state.c | 178 +++++++++++++++++++++-------------------------- include/linux/fs.h | 1 + include/linux/nfs_fs_i.h | 5 ++ 5 files changed, 118 insertions(+), 144 deletions(-) (limited to 'include/linux/fs.h') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 7c6f1d668fbd..ec1a22d7b876 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -128,6 +128,7 @@ struct nfs4_state_owner { struct nfs4_lock_state { struct list_head ls_locks; /* Other lock stateids */ + struct nfs4_state * ls_state; /* Pointer to open state */ fl_owner_t ls_owner; /* POSIX lock owner */ #define NFS_LOCK_INITIALIZED 1 int ls_flags; @@ -153,7 +154,7 @@ struct nfs4_state { unsigned long flags; /* Do we hold any locks? */ struct semaphore lock_sema; /* Serializes file locking operations */ - rwlock_t state_lock; /* Protects the lock_states list */ + spinlock_t state_lock; /* Protects the lock_states list */ nfs4_stateid stateid; @@ -225,12 +226,8 @@ extern void nfs4_close_state(struct nfs4_state *, mode_t); extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); extern void nfs4_schedule_state_recovery(struct nfs4_client *); -extern struct nfs4_lock_state *nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t); -extern struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t); -extern void nfs4_put_lock_state(struct nfs4_lock_state *state); +extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl); extern void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *ls); -extern void nfs4_notify_setlk(struct nfs4_state *, struct file_lock *, struct nfs4_lock_state *); -extern void nfs4_notify_unlck(struct nfs4_state *, struct file_lock *, struct nfs4_lock_state *); extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t); extern const nfs4_stateid zero_stateid; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index af80b5981486..0ddc20102d46 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2626,14 +2626,11 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock down_read(&clp->cl_sem); nlo.clientid = clp->cl_clientid; down(&state->lock_sema); - lsp = nfs4_find_lock_state(state, request->fl_owner); - if (lsp) - nlo.id = lsp->ls_id; - else { - spin_lock(&clp->cl_lock); - nlo.id = nfs4_alloc_lockowner_id(clp); - spin_unlock(&clp->cl_lock); - } + status = nfs4_set_lock_state(state, request); + if (status != 0) + goto out; + lsp = request->fl_u.nfs4_fl.owner; + nlo.id = lsp->ls_id; arg.u.lockt = &nlo; status = rpc_call_sync(server->client, &msg, 0); if (!status) { @@ -2654,8 +2651,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock request->fl_pid = 0; status = 0; } - if (lsp) - nfs4_put_lock_state(lsp); +out: up(&state->lock_sema); up_read(&clp->cl_sem); return status; @@ -2715,28 +2711,26 @@ static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock }; struct nfs4_lock_state *lsp; struct nfs_locku_opargs luargs; - int status = 0; + int status; down_read(&clp->cl_sem); down(&state->lock_sema); - lsp = nfs4_find_lock_state(state, request->fl_owner); - if (!lsp) + status = nfs4_set_lock_state(state, request); + if (status != 0) goto out; + lsp = request->fl_u.nfs4_fl.owner; /* We might have lost the locks! */ - if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0) { - luargs.seqid = lsp->ls_seqid; - memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid)); - arg.u.locku = &luargs; - status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); - nfs4_increment_lock_seqid(status, lsp); - } + if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0) + goto out; + luargs.seqid = lsp->ls_seqid; + memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid)); + arg.u.locku = &luargs; + status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); + nfs4_increment_lock_seqid(status, lsp); - if (status == 0) { + if (status == 0) memcpy(&lsp->ls_stateid, &res.u.stateid, sizeof(lsp->ls_stateid)); - nfs4_notify_unlck(state, request, lsp); - } - nfs4_put_lock_state(lsp); out: up(&state->lock_sema); if (status == 0) @@ -2762,7 +2756,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *r { struct inode *inode = state->inode; struct nfs_server *server = NFS_SERVER(inode); - struct nfs4_lock_state *lsp; + struct nfs4_lock_state *lsp = request->fl_u.nfs4_fl.owner; struct nfs_lockargs arg = { .fh = NFS_FH(inode), .type = nfs4_lck_type(cmd, request), @@ -2784,9 +2778,6 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *r }; int status; - lsp = nfs4_get_lock_state(state, request->fl_owner); - if (lsp == NULL) - return -ENOMEM; if (!(lsp->ls_flags & NFS_LOCK_INITIALIZED)) { struct nfs4_state_owner *owner = state->owner; struct nfs_open_to_lock otl = { @@ -2808,27 +2799,26 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *r * seqid mutating errors */ nfs4_increment_seqid(status, owner); up(&owner->so_sema); + if (status == 0) { + lsp->ls_flags |= NFS_LOCK_INITIALIZED; + lsp->ls_seqid++; + } } else { struct nfs_exist_lock el = { .seqid = lsp->ls_seqid, }; memcpy(&el.stateid, &lsp->ls_stateid, sizeof(el.stateid)); largs.u.exist_lock = ⪙ - largs.new_lock_owner = 0; arg.u.lock = &largs; status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); + /* increment seqid on success, and * seqid mutating errors*/ + nfs4_increment_lock_seqid(status, lsp); } - /* increment seqid on success, and * seqid mutating errors*/ - nfs4_increment_lock_seqid(status, lsp); /* save the returned stateid. */ - if (status == 0) { + if (status == 0) memcpy(&lsp->ls_stateid, &res.u.stateid, sizeof(nfs4_stateid)); - lsp->ls_flags |= NFS_LOCK_INITIALIZED; - if (!reclaim) - nfs4_notify_setlk(state, request, lsp); - } else if (status == -NFS4ERR_DENIED) + else if (status == -NFS4ERR_DENIED) status = -EAGAIN; - nfs4_put_lock_state(lsp); return status; } @@ -2869,7 +2859,9 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock down_read(&clp->cl_sem); down(&state->lock_sema); - status = _nfs4_do_setlk(state, cmd, request, 0); + status = nfs4_set_lock_state(state, request); + if (status == 0) + status = _nfs4_do_setlk(state, cmd, request, 0); up(&state->lock_sema); if (status == 0) { /* Note: we always want to sleep here! */ @@ -2927,7 +2919,6 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request) if (signalled()) break; } while(status < 0); - return status; } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 591ad1d51880..afe587d82f1e 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -360,7 +360,7 @@ nfs4_alloc_open_state(void) atomic_set(&state->count, 1); INIT_LIST_HEAD(&state->lock_states); init_MUTEX(&state->lock_sema); - rwlock_init(&state->state_lock); + spin_lock_init(&state->state_lock); return state; } @@ -542,16 +542,6 @@ __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) return NULL; } -struct nfs4_lock_state * -nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) -{ - struct nfs4_lock_state *lsp; - read_lock(&state->state_lock); - lsp = __nfs4_find_lock_state(state, fl_owner); - read_unlock(&state->state_lock); - return lsp; -} - /* * Return a compatible lock_state. If no initialized lock_state structure * exists, return an uninitialized one. @@ -568,14 +558,13 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f return NULL; lsp->ls_flags = 0; lsp->ls_seqid = 0; /* arbitrary */ - lsp->ls_id = -1; memset(lsp->ls_stateid.data, 0, sizeof(lsp->ls_stateid.data)); atomic_set(&lsp->ls_count, 1); lsp->ls_owner = fl_owner; - INIT_LIST_HEAD(&lsp->ls_locks); spin_lock(&clp->cl_lock); lsp->ls_id = nfs4_alloc_lockowner_id(clp); spin_unlock(&clp->cl_lock); + INIT_LIST_HEAD(&lsp->ls_locks); return lsp; } @@ -585,121 +574,112 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f * * The caller must be holding state->lock_sema and clp->cl_sem */ -struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner) +static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner) { - struct nfs4_lock_state * lsp; + struct nfs4_lock_state *lsp, *new = NULL; - lsp = nfs4_find_lock_state(state, owner); - if (lsp == NULL) - lsp = nfs4_alloc_lock_state(state, owner); + for(;;) { + spin_lock(&state->state_lock); + lsp = __nfs4_find_lock_state(state, owner); + if (lsp != NULL) + break; + if (new != NULL) { + new->ls_state = state; + list_add(&new->ls_locks, &state->lock_states); + set_bit(LK_STATE_IN_USE, &state->flags); + lsp = new; + new = NULL; + break; + } + spin_unlock(&state->state_lock); + new = nfs4_alloc_lock_state(state, owner); + if (new == NULL) + return NULL; + } + spin_unlock(&state->state_lock); + kfree(new); return lsp; } /* - * Byte-range lock aware utility to initialize the stateid of read/write - * requests. + * Release reference to lock_state, and free it if we see that + * it is no longer in use */ -void -nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner) +static void nfs4_put_lock_state(struct nfs4_lock_state *lsp) { - if (test_bit(LK_STATE_IN_USE, &state->flags)) { - struct nfs4_lock_state *lsp; + struct nfs4_state *state; - lsp = nfs4_find_lock_state(state, fl_owner); - if (lsp) { - memcpy(dst, &lsp->ls_stateid, sizeof(*dst)); - nfs4_put_lock_state(lsp); - return; - } - } - memcpy(dst, &state->stateid, sizeof(*dst)); + if (lsp == NULL) + return; + state = lsp->ls_state; + if (!atomic_dec_and_lock(&lsp->ls_count, &state->state_lock)) + return; + list_del(&lsp->ls_locks); + if (list_empty(&state->lock_states)) + clear_bit(LK_STATE_IN_USE, &state->flags); + spin_unlock(&state->state_lock); + kfree(lsp); } -/* -* Called with state->lock_sema and clp->cl_sem held. -*/ -void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp) +static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src) { - if (status == NFS_OK || seqid_mutating_err(-status)) - lsp->ls_seqid++; -} + struct nfs4_lock_state *lsp = src->fl_u.nfs4_fl.owner; -/* -* Check to see if the request lock (type FL_UNLK) effects the fl lock. -* -* fl and request must have the same posix owner -* -* return: -* 0 -> fl not effected by request -* 1 -> fl consumed by request -*/ + dst->fl_u.nfs4_fl.owner = lsp; + atomic_inc(&lsp->ls_count); +} -static int -nfs4_check_unlock(struct file_lock *fl, struct file_lock *request) +static void nfs4_fl_release_lock(struct file_lock *fl) { - if (fl->fl_start >= request->fl_start && fl->fl_end <= request->fl_end) - return 1; - return 0; + nfs4_put_lock_state(fl->fl_u.nfs4_fl.owner); } -/* - * Post an initialized lock_state on the state->lock_states list. - */ -void nfs4_notify_setlk(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp) +static struct file_lock_operations nfs4_fl_lock_ops = { + .fl_copy_lock = nfs4_fl_copy_lock, + .fl_release_private = nfs4_fl_release_lock, +}; + +int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl) { - if (!list_empty(&lsp->ls_locks)) - return; - atomic_inc(&lsp->ls_count); - write_lock(&state->state_lock); - list_add(&lsp->ls_locks, &state->lock_states); - set_bit(LK_STATE_IN_USE, &state->flags); - write_unlock(&state->state_lock); + struct nfs4_lock_state *lsp; + + if (fl->fl_ops != NULL) + return 0; + lsp = nfs4_get_lock_state(state, fl->fl_owner); + if (lsp == NULL) + return -ENOMEM; + fl->fl_u.nfs4_fl.owner = lsp; + fl->fl_ops = &nfs4_fl_lock_ops; + return 0; } -/* - * to decide to 'reap' lock state: - * 1) search i_flock for file_locks with fl.lock_state = to ls. - * 2) determine if unlock will consume found lock. - * if so, reap - * - * else, don't reap. - * +/* + * Byte-range lock aware utility to initialize the stateid of read/write + * requests. */ -void -nfs4_notify_unlck(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp) +void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner) { - struct inode *inode = state->inode; - struct file_lock *fl; + struct nfs4_lock_state *lsp; - for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { - if (!(fl->fl_flags & FL_POSIX)) - continue; - if (fl->fl_owner != lsp->ls_owner) - continue; - /* Exit if we find at least one lock which is not consumed */ - if (nfs4_check_unlock(fl,request) == 0) - return; - } + memcpy(dst, &state->stateid, sizeof(*dst)); + if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) + return; - write_lock(&state->state_lock); - list_del_init(&lsp->ls_locks); - if (list_empty(&state->lock_states)) - clear_bit(LK_STATE_IN_USE, &state->flags); - write_unlock(&state->state_lock); + spin_lock(&state->state_lock); + lsp = __nfs4_find_lock_state(state, fl_owner); + if (lsp != NULL && (lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0) + memcpy(dst, &lsp->ls_stateid, sizeof(*dst)); + spin_unlock(&state->state_lock); nfs4_put_lock_state(lsp); } /* - * Release reference to lock_state, and free it if we see that - * it is no longer in use - */ -void -nfs4_put_lock_state(struct nfs4_lock_state *lsp) +* Called with state->lock_sema and clp->cl_sem held. +*/ +void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp) { - if (!atomic_dec_and_test(&lsp->ls_count)) - return; - BUG_ON (!list_empty(&lsp->ls_locks)); - kfree(lsp); + if (status == NFS_OK || seqid_mutating_err(-status)) + lsp->ls_seqid++; } /* diff --git a/include/linux/fs.h b/include/linux/fs.h index 9b8b696d4f15..e5a8db00df29 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -674,6 +674,7 @@ struct file_lock { struct lock_manager_operations *fl_lmops; /* Callbacks for lockmanagers */ union { struct nfs_lock_info nfs_fl; + struct nfs4_lock_info nfs4_fl; } fl_u; }; diff --git a/include/linux/nfs_fs_i.h b/include/linux/nfs_fs_i.h index e9a749588a7b..e2c18dabff86 100644 --- a/include/linux/nfs_fs_i.h +++ b/include/linux/nfs_fs_i.h @@ -16,6 +16,11 @@ struct nfs_lock_info { struct nlm_lockowner *owner; }; +struct nfs4_lock_state; +struct nfs4_lock_info { + struct nfs4_lock_state *owner; +}; + /* * Lock flag values */ -- cgit v1.2.3 From 991114c6fa6a21d1fa4d544abe78592352860c82 Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Thu, 23 Jun 2005 00:09:01 -0700 Subject: [PATCH] fix for prune_icache()/forced final iput() races Based on analysis and a patch from Russ Weight There is a race condition that can occur if an inode is allocated and then released (using iput) during the ->fill_super functions. The race condition is between kswapd and mount. For most filesystems this can only happen in an error path when kswapd is running concurrently. For isofs, however, the error can occur in a more common code path (which is how the bug was found). The logic here is "we want final iput() to free inode *now* instead of letting it sit in cache if fs is going down or had not quite come up". The problem is with kswapd seeing such inodes in the middle of being killed and happily taking over. The clean solution would be to tell kswapd to leave those inodes alone and let our final iput deal with them. I.e. add a new flag (I_FORCED_FREEING), set it before write_inode_now() there and make prune_icache() leave those alone. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/inode.c | 16 ++++++++++------ include/linux/fs.h | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include/linux/fs.h') diff --git a/fs/inode.c b/fs/inode.c index 801fe7f36280..1f9a3a2b89bc 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -500,7 +500,7 @@ repeat: continue; if (!test(inode, data)) continue; - if (inode->i_state & (I_FREEING|I_CLEAR)) { + if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) { __wait_on_freeing_inode(inode); goto repeat; } @@ -525,7 +525,7 @@ repeat: continue; if (inode->i_sb != sb) continue; - if (inode->i_state & (I_FREEING|I_CLEAR)) { + if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) { __wait_on_freeing_inode(inode); goto repeat; } @@ -727,7 +727,7 @@ EXPORT_SYMBOL(iunique); struct inode *igrab(struct inode *inode) { spin_lock(&inode_lock); - if (!(inode->i_state & I_FREEING)) + if (!(inode->i_state & (I_FREEING|I_WILL_FREE))) __iget(inode); else /* @@ -1024,17 +1024,21 @@ static void generic_forget_inode(struct inode *inode) if (!(inode->i_state & (I_DIRTY|I_LOCK))) list_move(&inode->i_list, &inode_unused); inodes_stat.nr_unused++; - spin_unlock(&inode_lock); - if (!sb || (sb->s_flags & MS_ACTIVE)) + if (!sb || (sb->s_flags & MS_ACTIVE)) { + spin_unlock(&inode_lock); return; + } + inode->i_state |= I_WILL_FREE; + spin_unlock(&inode_lock); write_inode_now(inode, 1); spin_lock(&inode_lock); + inode->i_state &= ~I_WILL_FREE; inodes_stat.nr_unused--; hlist_del_init(&inode->i_hash); } list_del_init(&inode->i_list); list_del_init(&inode->i_sb_list); - inode->i_state|=I_FREEING; + inode->i_state |= I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); if (inode->i_data.nrpages) diff --git a/include/linux/fs.h b/include/linux/fs.h index e5a8db00df29..3622e952e98c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1025,6 +1025,7 @@ struct super_operations { #define I_FREEING 16 #define I_CLEAR 32 #define I_NEW 64 +#define I_WILL_FREE 128 #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) -- cgit v1.2.3 From bb93e3a52f8db7210258a1a2134cced0b78a46e1 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 23 Jun 2005 00:10:15 -0700 Subject: [PATCH] block: add unlocked_ioctl support for block devices This patch allows block device drivers to convert their ioctl functions to unlocked_ioctl() like character devices and other subsystems. All functions that were called with the BKL held before are still used that way, but I would not be surprised if it could be removed from the ioctl functions in drivers/block/ioctl.c themselves. As a side note, I found that compat_blkdev_ioctl() acquires the BKL as well, which looks like a bug. I have checked that every user of disk->fops->compat_ioctl() in the current git tree gets the BKL itself, so it could easily be removed from compat_blkdev_ioctl(). Signed-off-by: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/ioctl.c | 74 +++++++++++++++++++++++++++++++++++++-------------- fs/block_dev.c | 5 ++-- include/linux/fs.h | 1 + 3 files changed, 57 insertions(+), 23 deletions(-) (limited to 'include/linux/fs.h') diff --git a/drivers/block/ioctl.c b/drivers/block/ioctl.c index 6d7bcc9da9e7..6e278474f9a8 100644 --- a/drivers/block/ioctl.c +++ b/drivers/block/ioctl.c @@ -133,11 +133,9 @@ static int put_u64(unsigned long arg, u64 val) return put_user(val, (u64 __user *)arg); } -int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, - unsigned long arg) +static int blkdev_locked_ioctl(struct file *file, struct block_device *bdev, + unsigned cmd, unsigned long arg) { - struct block_device *bdev = inode->i_bdev; - struct gendisk *disk = bdev->bd_disk; struct backing_dev_info *bdi; int ret, n; @@ -190,36 +188,72 @@ int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, return put_ulong(arg, bdev->bd_inode->i_size >> 9); case BLKGETSIZE64: return put_u64(arg, bdev->bd_inode->i_size); + } + return -ENOIOCTLCMD; +} + +static int blkdev_driver_ioctl(struct inode *inode, struct file *file, + struct gendisk *disk, unsigned cmd, unsigned long arg) +{ + int ret; + if (disk->fops->unlocked_ioctl) + return disk->fops->unlocked_ioctl(file, cmd, arg); + + if (disk->fops->ioctl) { + lock_kernel(); + ret = disk->fops->ioctl(inode, file, cmd, arg); + unlock_kernel(); + return ret; + } + + return -ENOTTY; +} + +int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, + unsigned long arg) +{ + struct block_device *bdev = inode->i_bdev; + struct gendisk *disk = bdev->bd_disk; + int ret, n; + + switch(cmd) { case BLKFLSBUF: if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (disk->fops->ioctl) { - ret = disk->fops->ioctl(inode, file, cmd, arg); - /* -EINVAL to handle old uncorrected drivers */ - if (ret != -EINVAL && ret != -ENOTTY) - return ret; - } + + ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); + /* -EINVAL to handle old uncorrected drivers */ + if (ret != -EINVAL && ret != -ENOTTY) + return ret; + + lock_kernel(); fsync_bdev(bdev); invalidate_bdev(bdev, 0); + unlock_kernel(); return 0; + case BLKROSET: - if (disk->fops->ioctl) { - ret = disk->fops->ioctl(inode, file, cmd, arg); - /* -EINVAL to handle old uncorrected drivers */ - if (ret != -EINVAL && ret != -ENOTTY) - return ret; - } + ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); + /* -EINVAL to handle old uncorrected drivers */ + if (ret != -EINVAL && ret != -ENOTTY) + return ret; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (get_user(n, (int __user *)(arg))) return -EFAULT; + lock_kernel(); set_device_ro(bdev, n); + unlock_kernel(); return 0; - default: - if (disk->fops->ioctl) - return disk->fops->ioctl(inode, file, cmd, arg); } - return -ENOTTY; + + lock_kernel(); + ret = blkdev_locked_ioctl(file, bdev, cmd, arg); + unlock_kernel(); + if (ret != -ENOIOCTLCMD) + return ret; + + return blkdev_driver_ioctl(inode, file, disk, cmd, arg); } /* Most of the generic ioctls are handled in the normal fallback path. diff --git a/fs/block_dev.c b/fs/block_dev.c index c0cbd1bc1a02..e0df94c37b7e 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -777,8 +777,7 @@ static ssize_t blkdev_file_aio_write(struct kiocb *iocb, const char __user *buf, return generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos); } -static int block_ioctl(struct inode *inode, struct file *file, unsigned cmd, - unsigned long arg) +static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg) { return blkdev_ioctl(file->f_mapping->host, file, cmd, arg); } @@ -803,7 +802,7 @@ struct file_operations def_blk_fops = { .aio_write = blkdev_file_aio_write, .mmap = generic_file_mmap, .fsync = block_fsync, - .ioctl = block_ioctl, + .unlocked_ioctl = block_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_blkdev_ioctl, #endif diff --git a/include/linux/fs.h b/include/linux/fs.h index 3622e952e98c..9b1278e21279 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -884,6 +884,7 @@ struct block_device_operations { int (*open) (struct inode *, struct file *); int (*release) (struct inode *, struct file *); int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long); + long (*unlocked_ioctl) (struct file *, unsigned, unsigned long); long (*compat_ioctl) (struct file *, unsigned, unsigned long); int (*media_changed) (struct gendisk *); int (*revalidate_disk) (struct gendisk *); -- cgit v1.2.3 From 45778ca819accab1a4a3378b3566cab0f189164f Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 23 Jun 2005 00:10:17 -0700 Subject: [PATCH] Remove f_error field from struct file The following patch removes the f_error field and all checks of f_error. Trond said: f_error was introduced for NFS, and made sense when we were guaranteed always to have a file pointer around when write errors occurred. Since then, we have (for various reasons) had to introduce the nfs_open_context in order to track the file read/write state, and it made sense to move our f_error tracking there too. Signed-off-by: Christoph Lameter Acked-by: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfs/direct.c | 5 ----- fs/open.c | 16 ++++------------ include/linux/fs.h | 1 - mm/filemap.c | 6 ------ 4 files changed, 4 insertions(+), 24 deletions(-) (limited to 'include/linux/fs.h') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index d6a30c844de3..6537f2c4ae44 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -751,11 +751,6 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, retval = -EFAULT; if (!access_ok(VERIFY_READ, iov.iov_base, iov.iov_len)) goto out; - if (file->f_error) { - retval = file->f_error; - file->f_error = 0; - goto out; - } retval = -EFBIG; if (limit != RLIM_INFINITY) { if (pos >= limit) { diff --git a/fs/open.c b/fs/open.c index 2ebb72c1a876..5dd411b084bf 100644 --- a/fs/open.c +++ b/fs/open.c @@ -981,23 +981,15 @@ asmlinkage long sys_creat(const char __user * pathname, int mode) */ int filp_close(struct file *filp, fl_owner_t id) { - int retval; - - /* Report and clear outstanding errors */ - retval = filp->f_error; - if (retval) - filp->f_error = 0; + int retval = 0; if (!file_count(filp)) { printk(KERN_ERR "VFS: Close: file count is 0\n"); - return retval; + return 0; } - if (filp->f_op && filp->f_op->flush) { - int err = filp->f_op->flush(filp); - if (!retval) - retval = err; - } + if (filp->f_op && filp->f_op->flush) + retval = filp->f_op->flush(filp); dnotify_flush(filp, id); locks_remove_posix(filp, id); diff --git a/include/linux/fs.h b/include/linux/fs.h index 9b1278e21279..517bf4966bf5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -581,7 +581,6 @@ struct file { atomic_t f_count; unsigned int f_flags; mode_t f_mode; - int f_error; loff_t f_pos; struct fown_struct f_owner; unsigned int f_uid, f_gid; diff --git a/mm/filemap.c b/mm/filemap.c index 4a2fee2cb62b..a3598b542a31 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1827,12 +1827,6 @@ inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count, i if (unlikely(*pos < 0)) return -EINVAL; - if (unlikely(file->f_error)) { - int err = file->f_error; - file->f_error = 0; - return err; - } - if (!isblk) { /* FIXME: this is for backwards compatibility with 2.4 */ if (file->f_flags & O_APPEND) -- cgit v1.2.3 From 92198f7eaa5df3479341dd8fa20c2c81aa3b1e25 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 23 Jun 2005 22:00:59 -0700 Subject: [PATCH] pass iocb to dio_iodone_t XFS will have to look at iocb->private to fix aio+dio. No other filesystem is using the blockdev_direct_IO* end_io callback. Signed-off-by: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/direct-io.c | 2 +- fs/xfs/linux-2.6/xfs_aops.c | 3 ++- include/linux/fs.h | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include/linux/fs.h') diff --git a/fs/direct-io.c b/fs/direct-io.c index 1d55e7e67342..0d06097bc995 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -215,7 +215,7 @@ static struct page *dio_get_page(struct dio *dio) static void dio_complete(struct dio *dio, loff_t offset, ssize_t bytes) { if (dio->end_io && dio->result) - dio->end_io(dio->inode, offset, bytes, dio->map_bh.b_private); + dio->end_io(dio->iocb, offset, bytes, dio->map_bh.b_private); if (dio->lock_type == DIO_LOCKING) up_read(&dio->inode->i_alloc_sem); } diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 93ce257cd149..a3a4b5aaf5d9 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -149,11 +149,12 @@ linvfs_unwritten_convert( */ STATIC void linvfs_unwritten_convert_direct( - struct inode *inode, + struct kiocb *iocb, loff_t offset, ssize_t size, void *private) { + struct inode *inode = iocb->ki_filp->f_dentry->d_inode; ASSERT(!private || inode == (struct inode *)private); /* private indicates an unwritten extent lay beneath this IO */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 517bf4966bf5..83857d8070d3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -220,6 +220,7 @@ extern int dir_notify_enable; struct iovec; struct nameidata; +struct kiocb; struct pipe_inode_info; struct poll_table_struct; struct kstatfs; @@ -240,7 +241,7 @@ typedef int (get_block_t)(struct inode *inode, sector_t iblock, typedef int (get_blocks_t)(struct inode *inode, sector_t iblock, unsigned long max_blocks, struct buffer_head *bh_result, int create); -typedef void (dio_iodone_t)(struct inode *inode, loff_t offset, +typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, ssize_t bytes, void *private); /* @@ -302,7 +303,6 @@ struct iattr { struct page; struct address_space; struct writeback_control; -struct kiocb; struct address_space_operations { int (*writepage)(struct page *page, struct writeback_control *wbc); -- cgit v1.2.3 From 420edbcc09008342c7b2665453f6b370739aadb0 Mon Sep 17 00:00:00 2001 From: Carsten Otte Date: Thu, 23 Jun 2005 22:05:23 -0700 Subject: [PATCH] xip: bdev: execute in place This is the block device related part. The block device operation direct_access now has a struct block_device as first parameter. Signed-off-by: Carsten Otte Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/s390/block/dcssblk.c | 44 ++++++++++++++++++++++++++++++++++++++++---- include/linux/fs.h | 1 + 2 files changed, 41 insertions(+), 4 deletions(-) (limited to 'include/linux/fs.h') diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 16ab8d363ac6..6bc27d52326f 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -35,14 +35,17 @@ static int dcssblk_open(struct inode *inode, struct file *filp); static int dcssblk_release(struct inode *inode, struct file *filp); static int dcssblk_make_request(struct request_queue *q, struct bio *bio); +static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum, + unsigned long *data); static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; static int dcssblk_major; static struct block_device_operations dcssblk_devops = { - .owner = THIS_MODULE, - .open = dcssblk_open, - .release = dcssblk_release, + .owner = THIS_MODULE, + .open = dcssblk_open, + .release = dcssblk_release, + .direct_access = dcssblk_direct_access, }; static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, @@ -641,6 +644,20 @@ dcssblk_make_request(request_queue_t *q, struct bio *bio) /* Request beyond end of DCSS segment. */ goto fail; } + /* verify data transfer direction */ + if (dev_info->is_shared) { + switch (dev_info->segment_type) { + case SEG_TYPE_SR: + case SEG_TYPE_ER: + case SEG_TYPE_SC: + /* cannot write to these segments */ + if (bio_data_dir(bio) == WRITE) { + PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id); + goto fail; + } + } + } + index = (bio->bi_sector >> 3); bio_for_each_segment(bvec, bio, i) { page_addr = (unsigned long) @@ -661,7 +678,26 @@ dcssblk_make_request(request_queue_t *q, struct bio *bio) bio_endio(bio, bytes_done, 0); return 0; fail: - bio_io_error(bio, bytes_done); + bio_io_error(bio, bio->bi_size); + return 0; +} + +static int +dcssblk_direct_access (struct block_device *bdev, sector_t secnum, + unsigned long *data) +{ + struct dcssblk_dev_info *dev_info; + unsigned long pgoff; + + dev_info = bdev->bd_disk->private_data; + if (!dev_info) + return -ENODEV; + if (secnum % (PAGE_SIZE/512)) + return -EINVAL; + pgoff = secnum / (PAGE_SIZE / 512); + if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start) + return -ERANGE; + *data = (unsigned long) (dev_info->start+pgoff*PAGE_SIZE); return 0; } diff --git a/include/linux/fs.h b/include/linux/fs.h index 83857d8070d3..929bf8d20c87 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -885,6 +885,7 @@ struct block_device_operations { int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned, unsigned long); long (*compat_ioctl) (struct file *, unsigned, unsigned long); + int (*direct_access) (struct block_device *, sector_t, unsigned long *); int (*media_changed) (struct gendisk *); int (*revalidate_disk) (struct gendisk *); struct module *owner; -- cgit v1.2.3 From ceffc078528befc008c6f2c2c4decda79eabd534 Mon Sep 17 00:00:00 2001 From: Carsten Otte Date: Thu, 23 Jun 2005 22:05:25 -0700 Subject: [PATCH] xip: fs/mm: execute in place - generic_file* file operations do no longer have a xip/non-xip split - filemap_xip.c implements a new set of fops that require get_xip_page aop to work proper. all new fops are exported GPL-only (don't like to see whatever code use those except GPL modules) - __xip_unmap now uses page_check_address, which is no longer static in rmap.c, and defined in linux/rmap.h - mm/filemap.h is now much more clean, plainly having just Linus' inline funcs moved here from filemap.c - fix includes in filemap_xip to make it build cleanly on i386 Signed-off-by: Carsten Otte Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/open.c | 4 +- include/linux/fs.h | 18 ++ include/linux/rmap.h | 6 + mm/Makefile | 1 + mm/filemap.c | 74 +------ mm/filemap.h | 94 +++++++++ mm/filemap_xip.c | 581 +++++++++++++++++++++++++++++++++++++++++++++++++++ mm/rmap.c | 4 +- 8 files changed, 707 insertions(+), 75 deletions(-) create mode 100644 mm/filemap.h create mode 100644 mm/filemap_xip.c (limited to 'include/linux/fs.h') diff --git a/fs/open.c b/fs/open.c index 8ec63f735918..3f4a4286fdc4 100644 --- a/fs/open.c +++ b/fs/open.c @@ -808,7 +808,9 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) /* NB: we're sure to have correct a_ops only after f_op->open */ if (f->f_flags & O_DIRECT) { - if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO) { + if (!f->f_mapping->a_ops || + ((!f->f_mapping->a_ops->direct_IO) && + (!f->f_mapping->a_ops->get_xip_page))) { fput(f); f = ERR_PTR(-EINVAL); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 929bf8d20c87..79c0fafc0211 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -330,6 +330,8 @@ struct address_space_operations { int (*releasepage) (struct page *, int); ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov, loff_t offset, unsigned long nr_segs); + struct page* (*get_xip_page)(struct address_space *, sector_t, + int); }; struct backing_dev_info; @@ -1497,6 +1499,22 @@ extern loff_t remote_llseek(struct file *file, loff_t offset, int origin); extern int generic_file_open(struct inode * inode, struct file * filp); extern int nonseekable_open(struct inode * inode, struct file * filp); +#ifdef CONFIG_FS_XIP +extern ssize_t xip_file_aio_read(struct kiocb *iocb, char __user *buf, + size_t count, loff_t pos); +extern ssize_t xip_file_readv(struct file *filp, const struct iovec *iov, + unsigned long nr_segs, loff_t *ppos); +extern ssize_t xip_file_sendfile(struct file *in_file, loff_t *ppos, + size_t count, read_actor_t actor, + void *target); +extern int xip_file_mmap(struct file * file, struct vm_area_struct * vma); +extern ssize_t xip_file_aio_write(struct kiocb *iocb, const char __user *buf, + size_t count, loff_t pos); +extern ssize_t xip_file_writev(struct file *file, const struct iovec *iov, + unsigned long nr_segs, loff_t *ppos); +extern int xip_truncate_page(struct address_space *mapping, loff_t from); +#endif + static inline void do_generic_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc, read_actor_t actor) diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 11b484e37ac9..e80fb7ee6efd 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -92,6 +92,12 @@ static inline void page_dup_rmap(struct page *page) int page_referenced(struct page *, int is_locked, int ignore_token); int try_to_unmap(struct page *); +/* + * Called from mm/filemap_xip.c to unmap empty zero page + */ +pte_t *page_check_address(struct page *, struct mm_struct *, unsigned long); + + /* * Used by swapoff to help locate where page is expected in vma. */ diff --git a/mm/Makefile b/mm/Makefile index 8f70ffd763c8..4cd69e3ce421 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_SPARSEMEM) += sparse.o obj-$(CONFIG_SHMEM) += shmem.o obj-$(CONFIG_TINY_SHMEM) += tiny-shmem.o +obj-$(CONFIG_FS_XIP) += filemap_xip.o diff --git a/mm/filemap.c b/mm/filemap.c index a3598b542a31..7332194d7afd 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -28,6 +28,7 @@ #include #include #include +#include "filemap.h" /* * FIXME: remove all knowledge of the buffer layer from the core VM */ @@ -1714,32 +1715,7 @@ int remove_suid(struct dentry *dentry) } EXPORT_SYMBOL(remove_suid); -/* - * Copy as much as we can into the page and return the number of bytes which - * were sucessfully copied. If a fault is encountered then clear the page - * out to (offset+bytes) and return the number of bytes which were copied. - */ -static inline size_t -filemap_copy_from_user(struct page *page, unsigned long offset, - const char __user *buf, unsigned bytes) -{ - char *kaddr; - int left; - - kaddr = kmap_atomic(page, KM_USER0); - left = __copy_from_user_inatomic(kaddr + offset, buf, bytes); - kunmap_atomic(kaddr, KM_USER0); - - if (left != 0) { - /* Do it the slow way */ - kaddr = kmap(page); - left = __copy_from_user(kaddr + offset, buf, bytes); - kunmap(page); - } - return bytes - left; -} - -static size_t +size_t __filemap_copy_from_user_iovec(char *vaddr, const struct iovec *iov, size_t base, size_t bytes) { @@ -1766,52 +1742,6 @@ __filemap_copy_from_user_iovec(char *vaddr, return copied - left; } -/* - * This has the same sideeffects and return value as filemap_copy_from_user(). - * The difference is that on a fault we need to memset the remainder of the - * page (out to offset+bytes), to emulate filemap_copy_from_user()'s - * single-segment behaviour. - */ -static inline size_t -filemap_copy_from_user_iovec(struct page *page, unsigned long offset, - const struct iovec *iov, size_t base, size_t bytes) -{ - char *kaddr; - size_t copied; - - kaddr = kmap_atomic(page, KM_USER0); - copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, - base, bytes); - kunmap_atomic(kaddr, KM_USER0); - if (copied != bytes) { - kaddr = kmap(page); - copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, - base, bytes); - kunmap(page); - } - return copied; -} - -static inline void -filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes) -{ - const struct iovec *iov = *iovp; - size_t base = *basep; - - while (bytes) { - int copy = min(bytes, iov->iov_len - base); - - bytes -= copy; - base += copy; - if (iov->iov_len == base) { - iov++; - base = 0; - } - } - *iovp = iov; - *basep = base; -} - /* * Performs necessary checks before doing a write * diff --git a/mm/filemap.h b/mm/filemap.h new file mode 100644 index 000000000000..c2d0546a57eb --- /dev/null +++ b/mm/filemap.h @@ -0,0 +1,94 @@ +/* + * linux/mm/filemap.h + * + * Copyright (C) 1994-1999 Linus Torvalds + */ + +#ifndef __FILEMAP_H +#define __FILEMAP_H + +#include +#include +#include +#include +#include +#include +#include + +extern size_t +__filemap_copy_from_user_iovec(char *vaddr, + const struct iovec *iov, + size_t base, + size_t bytes); + +/* + * Copy as much as we can into the page and return the number of bytes which + * were sucessfully copied. If a fault is encountered then clear the page + * out to (offset+bytes) and return the number of bytes which were copied. + */ +static inline size_t +filemap_copy_from_user(struct page *page, unsigned long offset, + const char __user *buf, unsigned bytes) +{ + char *kaddr; + int left; + + kaddr = kmap_atomic(page, KM_USER0); + left = __copy_from_user_inatomic(kaddr + offset, buf, bytes); + kunmap_atomic(kaddr, KM_USER0); + + if (left != 0) { + /* Do it the slow way */ + kaddr = kmap(page); + left = __copy_from_user(kaddr + offset, buf, bytes); + kunmap(page); + } + return bytes - left; +} + +/* + * This has the same sideeffects and return value as filemap_copy_from_user(). + * The difference is that on a fault we need to memset the remainder of the + * page (out to offset+bytes), to emulate filemap_copy_from_user()'s + * single-segment behaviour. + */ +static inline size_t +filemap_copy_from_user_iovec(struct page *page, unsigned long offset, + const struct iovec *iov, size_t base, size_t bytes) +{ + char *kaddr; + size_t copied; + + kaddr = kmap_atomic(page, KM_USER0); + copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, + base, bytes); + kunmap_atomic(kaddr, KM_USER0); + if (copied != bytes) { + kaddr = kmap(page); + copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, + base, bytes); + kunmap(page); + } + return copied; +} + +static inline void +filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes) +{ + const struct iovec *iov = *iovp; + size_t base = *basep; + + while (bytes) { + int copy = min(bytes, iov->iov_len - base); + + bytes -= copy; + base += copy; + if (iov->iov_len == base) { + iov++; + base = 0; + } + } + *iovp = iov; + *basep = base; +} +#endif diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c new file mode 100644 index 000000000000..7d63acd48817 --- /dev/null +++ b/mm/filemap_xip.c @@ -0,0 +1,581 @@ +/* + * linux/mm/filemap_xip.c + * + * Copyright (C) 2005 IBM Corporation + * Author: Carsten Otte + * + * derived from linux/mm/filemap.c - Copyright (C) Linus Torvalds + * + */ + +#include +#include +#include +#include +#include +#include +#include "filemap.h" + +/* + * This is a file read routine for execute in place files, and uses + * the mapping->a_ops->get_xip_page() function for the actual low-level + * stuff. + * + * Note the struct file* is not used at all. It may be NULL. + */ +static void +do_xip_mapping_read(struct address_space *mapping, + struct file_ra_state *_ra, + struct file *filp, + loff_t *ppos, + read_descriptor_t *desc, + read_actor_t actor) +{ + struct inode *inode = mapping->host; + unsigned long index, end_index, offset; + loff_t isize; + + BUG_ON(!mapping->a_ops->get_xip_page); + + index = *ppos >> PAGE_CACHE_SHIFT; + offset = *ppos & ~PAGE_CACHE_MASK; + + isize = i_size_read(inode); + if (!isize) + goto out; + + end_index = (isize - 1) >> PAGE_CACHE_SHIFT; + for (;;) { + struct page *page; + unsigned long nr, ret; + + /* nr is the maximum number of bytes to copy from this page */ + nr = PAGE_CACHE_SIZE; + if (index >= end_index) { + if (index > end_index) + goto out; + nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1; + if (nr <= offset) { + goto out; + } + } + nr = nr - offset; + + page = mapping->a_ops->get_xip_page(mapping, + index*(PAGE_SIZE/512), 0); + if (!page) + goto no_xip_page; + if (unlikely(IS_ERR(page))) { + if (PTR_ERR(page) == -ENODATA) { + /* sparse */ + page = virt_to_page(empty_zero_page); + } else { + desc->error = PTR_ERR(page); + goto out; + } + } else + BUG_ON(!PageUptodate(page)); + + /* If users can be writing to this page using arbitrary + * virtual addresses, take care about potential aliasing + * before reading the page on the kernel side. + */ + if (mapping_writably_mapped(mapping)) + flush_dcache_page(page); + + /* + * Ok, we have the page, and it's up-to-date, so + * now we can copy it to user space... + * + * The actor routine returns how many bytes were actually used.. + * NOTE! This may not be the same as how much of a user buffer + * we filled up (we may be padding etc), so we can only update + * "pos" here (the actor routine has to update the user buffer + * pointers and the remaining count). + */ + ret = actor(desc, page, offset, nr); + offset += ret; + index += offset >> PAGE_CACHE_SHIFT; + offset &= ~PAGE_CACHE_MASK; + + if (ret == nr && desc->count) + continue; + goto out; + +no_xip_page: + /* Did not get the page. Report it */ + desc->error = -EIO; + goto out; + } + +out: + *ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset; + if (filp) + file_accessed(filp); +} + +/* + * This is the "read()" routine for all filesystems + * that uses the get_xip_page address space operation. + */ +static ssize_t +__xip_file_aio_read(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t *ppos) +{ + struct file *filp = iocb->ki_filp; + ssize_t retval; + unsigned long seg; + size_t count; + + count = 0; + for (seg = 0; seg < nr_segs; seg++) { + const struct iovec *iv = &iov[seg]; + + /* + * If any segment has a negative length, or the cumulative + * length ever wraps negative then return -EINVAL. + */ + count += iv->iov_len; + if (unlikely((ssize_t)(count|iv->iov_len) < 0)) + return -EINVAL; + if (access_ok(VERIFY_WRITE, iv->iov_base, iv->iov_len)) + continue; + if (seg == 0) + return -EFAULT; + nr_segs = seg; + count -= iv->iov_len; /* This segment is no good */ + break; + } + + retval = 0; + if (count) { + for (seg = 0; seg < nr_segs; seg++) { + read_descriptor_t desc; + + desc.written = 0; + desc.arg.buf = iov[seg].iov_base; + desc.count = iov[seg].iov_len; + if (desc.count == 0) + continue; + desc.error = 0; + do_xip_mapping_read(filp->f_mapping, &filp->f_ra, filp, + ppos, &desc, file_read_actor); + retval += desc.written; + if (!retval) { + retval = desc.error; + break; + } + } + } + return retval; +} + +ssize_t +xip_file_aio_read(struct kiocb *iocb, char __user *buf, size_t count, + loff_t pos) +{ + struct iovec local_iov = { .iov_base = buf, .iov_len = count }; + + BUG_ON(iocb->ki_pos != pos); + return __xip_file_aio_read(iocb, &local_iov, 1, &iocb->ki_pos); +} +EXPORT_SYMBOL_GPL(xip_file_aio_read); + +ssize_t +xip_file_readv(struct file *filp, const struct iovec *iov, + unsigned long nr_segs, loff_t *ppos) +{ + struct kiocb kiocb; + + init_sync_kiocb(&kiocb, filp); + return __xip_file_aio_read(&kiocb, iov, nr_segs, ppos); +} +EXPORT_SYMBOL_GPL(xip_file_readv); + +ssize_t +xip_file_sendfile(struct file *in_file, loff_t *ppos, + size_t count, read_actor_t actor, void *target) +{ + read_descriptor_t desc; + + if (!count) + return 0; + + desc.written = 0; + desc.count = count; + desc.arg.data = target; + desc.error = 0; + + do_xip_mapping_read(in_file->f_mapping, &in_file->f_ra, in_file, + ppos, &desc, actor); + if (desc.written) + return desc.written; + return desc.error; +} +EXPORT_SYMBOL_GPL(xip_file_sendfile); + +/* + * __xip_unmap is invoked from xip_unmap and + * xip_write + * + * This function walks all vmas of the address_space and unmaps the + * empty_zero_page when found at pgoff. Should it go in rmap.c? + */ +static void +__xip_unmap (struct address_space * mapping, + unsigned long pgoff) +{ + struct vm_area_struct *vma; + struct mm_struct *mm; + struct prio_tree_iter iter; + unsigned long address; + pte_t *pte; + pte_t pteval; + + spin_lock(&mapping->i_mmap_lock); + vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { + mm = vma->vm_mm; + address = vma->vm_start + + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT); + BUG_ON(address < vma->vm_start || address >= vma->vm_end); + /* + * We need the page_table_lock to protect us from page faults, + * munmap, fork, etc... + */ + pte = page_check_address(virt_to_page(empty_zero_page), mm, + address); + if (!IS_ERR(pte)) { + /* Nuke the page table entry. */ + flush_cache_page(vma, address, pte_pfn(pte)); + pteval = ptep_clear_flush(vma, address, pte); + BUG_ON(pte_dirty(pteval)); + pte_unmap(pte); + spin_unlock(&mm->page_table_lock); + } + } + spin_unlock(&mapping->i_mmap_lock); +} + +/* + * xip_nopage() is invoked via the vma operations vector for a + * mapped memory region to read in file data during a page fault. + * + * This function is derived from filemap_nopage, but used for execute in place + */ +static struct page * +xip_file_nopage(struct vm_area_struct * area, + unsigned long address, + int *type) +{ + struct file *file = area->vm_file; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + struct page *page; + unsigned long size, pgoff, endoff; + + pgoff = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + + area->vm_pgoff; + endoff = ((area->vm_end - area->vm_start) >> PAGE_CACHE_SHIFT) + + area->vm_pgoff; + + size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + if (pgoff >= size) { + return NULL; + } + + page = mapping->a_ops->get_xip_page(mapping, pgoff*(PAGE_SIZE/512), 0); + if (!IS_ERR(page)) { + BUG_ON(!PageUptodate(page)); + return page; + } + if (PTR_ERR(page) != -ENODATA) + return NULL; + + /* sparse block */ + if ((area->vm_flags & (VM_WRITE | VM_MAYWRITE)) && + (area->vm_flags & (VM_SHARED| VM_MAYSHARE)) && + (!(mapping->host->i_sb->s_flags & MS_RDONLY))) { + /* maybe shared writable, allocate new block */ + page = mapping->a_ops->get_xip_page (mapping, + pgoff*(PAGE_SIZE/512), 1); + if (IS_ERR(page)) + return NULL; + BUG_ON(!PageUptodate(page)); + /* unmap page at pgoff from all other vmas */ + __xip_unmap(mapping, pgoff); + } else { + /* not shared and writable, use empty_zero_page */ + page = virt_to_page(empty_zero_page); + } + + return page; +} + +static struct vm_operations_struct xip_file_vm_ops = { + .nopage = xip_file_nopage, +}; + +int xip_file_mmap(struct file * file, struct vm_area_struct * vma) +{ + BUG_ON(!file->f_mapping->a_ops->get_xip_page); + + file_accessed(file); + vma->vm_ops = &xip_file_vm_ops; + return 0; +} +EXPORT_SYMBOL_GPL(xip_file_mmap); + +static ssize_t +do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos, loff_t *ppos, + size_t count) +{ + struct file *file = iocb->ki_filp; + struct address_space * mapping = file->f_mapping; + struct address_space_operations *a_ops = mapping->a_ops; + struct inode *inode = mapping->host; + long status = 0; + struct page *page; + size_t bytes; + const struct iovec *cur_iov = iov; /* current iovec */ + size_t iov_base = 0; /* offset in the current iovec */ + char __user *buf; + ssize_t written = 0; + + BUG_ON(!mapping->a_ops->get_xip_page); + + buf = iov->iov_base; + do { + unsigned long index; + unsigned long offset; + size_t copied; + + offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */ + index = pos >> PAGE_CACHE_SHIFT; + bytes = PAGE_CACHE_SIZE - offset; + if (bytes > count) + bytes = count; + + /* + * Bring in the user page that we will copy from _first_. + * Otherwise there's a nasty deadlock on copying from the + * same page as we're writing to, without it being marked + * up-to-date. + */ + fault_in_pages_readable(buf, bytes); + + page = a_ops->get_xip_page(mapping, + index*(PAGE_SIZE/512), 0); + if (IS_ERR(page) && (PTR_ERR(page) == -ENODATA)) { + /* we allocate a new page unmap it */ + page = a_ops->get_xip_page(mapping, + index*(PAGE_SIZE/512), 1); + if (!IS_ERR(page)) + /* unmap page at pgoff from all other vmas */ + __xip_unmap(mapping, index); + + } + + if (IS_ERR(page)) { + status = PTR_ERR(page); + break; + } + + BUG_ON(!PageUptodate(page)); + + if (likely(nr_segs == 1)) + copied = filemap_copy_from_user(page, offset, + buf, bytes); + else + copied = filemap_copy_from_user_iovec(page, offset, + cur_iov, iov_base, bytes); + flush_dcache_page(page); + if (likely(copied > 0)) { + status = copied; + + if (status >= 0) { + written += status; + count -= status; + pos += status; + buf += status; + if (unlikely(nr_segs > 1)) + filemap_set_next_iovec(&cur_iov, + &iov_base, status); + } + } + if (unlikely(copied != bytes)) + if (status >= 0) + status = -EFAULT; + if (status < 0) + break; + } while (count); + *ppos = pos; + /* + * No need to use i_size_read() here, the i_size + * cannot change under us because we hold i_sem. + */ + if (pos > inode->i_size) { + i_size_write(inode, pos); + mark_inode_dirty(inode); + } + + return written ? written : status; +} + +static ssize_t +xip_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t *ppos) +{ + struct file *file = iocb->ki_filp; + struct address_space * mapping = file->f_mapping; + size_t ocount; /* original count */ + size_t count; /* after file limit checks */ + struct inode *inode = mapping->host; + unsigned long seg; + loff_t pos; + ssize_t written; + ssize_t err; + + ocount = 0; + for (seg = 0; seg < nr_segs; seg++) { + const struct iovec *iv = &iov[seg]; + + /* + * If any segment has a negative length, or the cumulative + * length ever wraps negative then return -EINVAL. + */ + ocount += iv->iov_len; + if (unlikely((ssize_t)(ocount|iv->iov_len) < 0)) + return -EINVAL; + if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len)) + continue; + if (seg == 0) + return -EFAULT; + nr_segs = seg; + ocount -= iv->iov_len; /* This segment is no good */ + break; + } + + count = ocount; + pos = *ppos; + + vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); + + written = 0; + + err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); + if (err) + goto out; + + if (count == 0) + goto out; + + err = remove_suid(file->f_dentry); + if (err) + goto out; + + inode_update_time(inode, 1); + + /* use execute in place to copy directly to disk */ + written = do_xip_file_write (iocb, iov, + nr_segs, pos, ppos, count); + out: + return written ? written : err; +} + +static ssize_t +__xip_file_write_nolock(struct file *file, const struct iovec *iov, + unsigned long nr_segs, loff_t *ppos) +{ + struct kiocb kiocb; + + init_sync_kiocb(&kiocb, file); + return xip_file_aio_write_nolock(&kiocb, iov, nr_segs, ppos); +} + +ssize_t +xip_file_aio_write(struct kiocb *iocb, const char __user *buf, + size_t count, loff_t pos) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + ssize_t ret; + struct iovec local_iov = { .iov_base = (void __user *)buf, + .iov_len = count }; + + BUG_ON(iocb->ki_pos != pos); + + down(&inode->i_sem); + ret = xip_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos); + up(&inode->i_sem); + return ret; +} +EXPORT_SYMBOL_GPL(xip_file_aio_write); + +ssize_t xip_file_writev(struct file *file, const struct iovec *iov, + unsigned long nr_segs, loff_t *ppos) +{ + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + ssize_t ret; + + down(&inode->i_sem); + ret = __xip_file_write_nolock(file, iov, nr_segs, ppos); + up(&inode->i_sem); + return ret; +} +EXPORT_SYMBOL_GPL(xip_file_writev); + +/* + * truncate a page used for execute in place + * functionality is analog to block_truncate_page but does use get_xip_page + * to get the page instead of page cache + */ +int +xip_truncate_page(struct address_space *mapping, loff_t from) +{ + pgoff_t index = from >> PAGE_CACHE_SHIFT; + unsigned offset = from & (PAGE_CACHE_SIZE-1); + unsigned blocksize; + unsigned length; + struct page *page; + void *kaddr; + int err; + + BUG_ON(!mapping->a_ops->get_xip_page); + + blocksize = 1 << mapping->host->i_blkbits; + length = offset & (blocksize - 1); + + /* Block boundary? Nothing to do */ + if (!length) + return 0; + + length = blocksize - length; + + page = mapping->a_ops->get_xip_page(mapping, + index*(PAGE_SIZE/512), 0); + err = -ENOMEM; + if (!page) + goto out; + if (unlikely(IS_ERR(page))) { + if (PTR_ERR(page) == -ENODATA) { + /* Hole? No need to truncate */ + return 0; + } else { + err = PTR_ERR(page); + goto out; + } + } else + BUG_ON(!PageUptodate(page)); + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + offset, 0, length); + kunmap_atomic(kaddr, KM_USER0); + + flush_dcache_page(page); + err = 0; +out: + return err; +} +EXPORT_SYMBOL_GPL(xip_truncate_page); diff --git a/mm/rmap.c b/mm/rmap.c index 89770bd25f31..08ac5c7fa91f 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -247,8 +247,8 @@ unsigned long page_address_in_vma(struct page *page, struct vm_area_struct *vma) * * On success returns with mapped pte and locked mm->page_table_lock. */ -static pte_t *page_check_address(struct page *page, struct mm_struct *mm, - unsigned long address) +pte_t *page_check_address(struct page *page, struct mm_struct *mm, + unsigned long address) { pgd_t *pgd; pud_t *pud; -- cgit v1.2.3 From 6d79125bba55ee82701f1c7d4ebbc1aa20ecbe4e Mon Sep 17 00:00:00 2001 From: Carsten Otte Date: Thu, 23 Jun 2005 22:05:26 -0700 Subject: [PATCH] xip: ext2: execute in place These are the ext2 related parts. Ext2 now uses the xip_* file operations along with the get_xip_page aop when mounted with -o xip. Signed-off-by: Carsten Otte Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/Kconfig | 17 +++++++++++ fs/ext2/Makefile | 1 + fs/ext2/ext2.h | 2 ++ fs/ext2/file.c | 18 +++++++++++ fs/ext2/inode.c | 31 ++++++++++++++++--- fs/ext2/namei.c | 12 ++++++-- fs/ext2/super.c | 27 ++++++++++++++++- fs/ext2/xip.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/ext2/xip.h | 25 ++++++++++++++++ include/linux/ext2_fs.h | 25 ++++++++-------- include/linux/fs.h | 5 ++++ 11 files changed, 223 insertions(+), 20 deletions(-) create mode 100644 fs/ext2/xip.c create mode 100644 fs/ext2/xip.h (limited to 'include/linux/fs.h') diff --git a/fs/Kconfig b/fs/Kconfig index 5c704d05627a..8157f2e2d515 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -50,6 +50,23 @@ config EXT2_FS_SECURITY If you are not using a security module that requires using extended attributes for file security labels, say N. +config EXT2_FS_XIP + bool "Ext2 execute in place support" + depends on EXT2_FS + help + Execute in place can be used on memory-backed block devices. If you + enable this option, you can select to mount block devices which are + capable of this feature without using the page cache. + + If you do not use a block device that is capable of using this, + or if unsure, say N. + +config FS_XIP +# execute in place + bool + depends on EXT2_FS_XIP + default y + config EXT3_FS tristate "Ext3 journalling file system support" help diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile index ee240a14e70f..c5d02da73bc3 100644 --- a/fs/ext2/Makefile +++ b/fs/ext2/Makefile @@ -10,3 +10,4 @@ ext2-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o ext2-$(CONFIG_EXT2_FS_SECURITY) += xattr_security.o +ext2-$(CONFIG_EXT2_FS_XIP) += xip.o diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 8f0fd726c3f1..eed521d22cf0 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -147,9 +147,11 @@ extern struct file_operations ext2_dir_operations; /* file.c */ extern struct inode_operations ext2_file_inode_operations; extern struct file_operations ext2_file_operations; +extern struct file_operations ext2_xip_file_operations; /* inode.c */ extern struct address_space_operations ext2_aops; +extern struct address_space_operations ext2_aops_xip; extern struct address_space_operations ext2_nobh_aops; /* namei.c */ diff --git a/fs/ext2/file.c b/fs/ext2/file.c index f5e86141ec54..2b3d572365af 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -55,6 +55,24 @@ struct file_operations ext2_file_operations = { .sendfile = generic_file_sendfile, }; +#ifdef CONFIG_EXT2_FS_XIP +struct file_operations ext2_xip_file_operations = { + .llseek = generic_file_llseek, + .read = do_sync_read, + .write = do_sync_write, + .aio_read = xip_file_aio_read, + .aio_write = xip_file_aio_write, + .ioctl = ext2_ioctl, + .mmap = xip_file_mmap, + .open = generic_file_open, + .release = ext2_release_file, + .fsync = ext2_sync_file, + .readv = xip_file_readv, + .writev = xip_file_writev, + .sendfile = xip_file_sendfile, +}; +#endif + struct inode_operations ext2_file_inode_operations = { .truncate = ext2_truncate, #ifdef CONFIG_EXT2_FS_XATTR diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index a50d9db4b6e4..53dceb0c6593 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -33,6 +33,7 @@ #include #include "ext2.h" #include "acl.h" +#include "xip.h" MODULE_AUTHOR("Remy Card and others"); MODULE_DESCRIPTION("Second Extended Filesystem"); @@ -594,6 +595,16 @@ out: if (err) goto cleanup; + if (ext2_use_xip(inode->i_sb)) { + /* + * we need to clear the block + */ + err = ext2_clear_xip_target (inode, + le32_to_cpu(chain[depth-1].key)); + if (err) + goto cleanup; + } + if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0) goto changed; @@ -691,6 +702,11 @@ struct address_space_operations ext2_aops = { .writepages = ext2_writepages, }; +struct address_space_operations ext2_aops_xip = { + .bmap = ext2_bmap, + .get_xip_page = ext2_get_xip_page, +}; + struct address_space_operations ext2_nobh_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages, @@ -910,7 +926,9 @@ void ext2_truncate (struct inode * inode) iblock = (inode->i_size + blocksize-1) >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); - if (test_opt(inode->i_sb, NOBH)) + if (mapping_is_xip(inode->i_mapping)) + xip_truncate_page(inode->i_mapping, inode->i_size); + else if (test_opt(inode->i_sb, NOBH)) nobh_truncate_page(inode->i_mapping, inode->i_size); else block_truncate_page(inode->i_mapping, @@ -1110,11 +1128,16 @@ void ext2_read_inode (struct inode * inode) if (S_ISREG(inode->i_mode)) { inode->i_op = &ext2_file_inode_operations; - inode->i_fop = &ext2_file_operations; - if (test_opt(inode->i_sb, NOBH)) + if (ext2_use_xip(inode->i_sb)) { + inode->i_mapping->a_ops = &ext2_aops_xip; + inode->i_fop = &ext2_xip_file_operations; + } else if (test_opt(inode->i_sb, NOBH)) { inode->i_mapping->a_ops = &ext2_nobh_aops; - else + inode->i_fop = &ext2_file_operations; + } else { inode->i_mapping->a_ops = &ext2_aops; + inode->i_fop = &ext2_file_operations; + } } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ext2_dir_inode_operations; inode->i_fop = &ext2_dir_operations; diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 3176b3d3ffa8..c5513953c825 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -34,6 +34,7 @@ #include "ext2.h" #include "xattr.h" #include "acl.h" +#include "xip.h" /* * Couple of helper functions - make the code slightly cleaner. @@ -127,11 +128,16 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, st int err = PTR_ERR(inode); if (!IS_ERR(inode)) { inode->i_op = &ext2_file_inode_operations; - inode->i_fop = &ext2_file_operations; - if (test_opt(inode->i_sb, NOBH)) + if (ext2_use_xip(inode->i_sb)) { + inode->i_mapping->a_ops = &ext2_aops_xip; + inode->i_fop = &ext2_xip_file_operations; + } else if (test_opt(inode->i_sb, NOBH)) { inode->i_mapping->a_ops = &ext2_nobh_aops; - else + inode->i_fop = &ext2_file_operations; + } else { inode->i_mapping->a_ops = &ext2_aops; + inode->i_fop = &ext2_file_operations; + } mark_inode_dirty(inode); err = ext2_add_nondir(dentry, inode); } diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 661c3d98d946..876e391f2871 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -31,6 +31,7 @@ #include "ext2.h" #include "xattr.h" #include "acl.h" +#include "xip.h" static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es); @@ -257,7 +258,7 @@ enum { Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid, Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_nouid32, Opt_check, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_nobh, - Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, + Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, }; @@ -286,6 +287,7 @@ static match_table_t tokens = { {Opt_nouser_xattr, "nouser_xattr"}, {Opt_acl, "acl"}, {Opt_noacl, "noacl"}, + {Opt_xip, "xip"}, {Opt_ignore, "grpquota"}, {Opt_ignore, "noquota"}, {Opt_ignore, "quota"}, @@ -397,6 +399,13 @@ static int parse_options (char * options, printk("EXT2 (no)acl options not supported\n"); break; #endif + case Opt_xip: +#ifdef CONFIG_EXT2_FS_XIP + set_opt (sbi->s_mount_opt, XIP); +#else + printk("EXT2 xip option not supported\n"); +#endif + break; case Opt_ignore: break; default: @@ -640,6 +649,9 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); + ext2_xip_verify_sb(sb); /* see if bdev supports xip, unset + EXT2_MOUNT_XIP if not */ + if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) || @@ -668,6 +680,13 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size); + if ((ext2_use_xip(sb)) && ((blocksize != PAGE_SIZE) || + (sb->s_blocksize != blocksize))) { + if (!silent) + printk("XIP: Unsupported blocksize\n"); + goto failed_mount; + } + /* If the blocksize doesn't match, re-read the thing.. */ if (sb->s_blocksize != blocksize) { brelse(bh); @@ -916,6 +935,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) { struct ext2_sb_info * sbi = EXT2_SB(sb); struct ext2_super_block * es; + unsigned long old_mount_opt = sbi->s_mount_opt; /* * Allow the "check" option to be passed as a remount option. @@ -927,6 +947,11 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) ((sbi->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); es = sbi->s_es; + if (((sbi->s_mount_opt & EXT2_MOUNT_XIP) != + (old_mount_opt & EXT2_MOUNT_XIP)) && + invalidate_inodes(sb)) + ext2_warning(sb, __FUNCTION__, "busy inodes while remounting "\ + "xip remain in cache (no functional problem)"); if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; if (*flags & MS_RDONLY) { diff --git a/fs/ext2/xip.c b/fs/ext2/xip.c new file mode 100644 index 000000000000..d44431d1a338 --- /dev/null +++ b/fs/ext2/xip.c @@ -0,0 +1,80 @@ +/* + * linux/fs/ext2/xip.c + * + * Copyright (C) 2005 IBM Corporation + * Author: Carsten Otte (cotte@de.ibm.com) + */ + +#include +#include +#include +#include +#include +#include +#include "ext2.h" +#include "xip.h" + +static inline int +__inode_direct_access(struct inode *inode, sector_t sector, unsigned long *data) { + BUG_ON(!inode->i_sb->s_bdev->bd_disk->fops->direct_access); + return inode->i_sb->s_bdev->bd_disk->fops + ->direct_access(inode->i_sb->s_bdev,sector,data); +} + +int +ext2_clear_xip_target(struct inode *inode, int block) { + sector_t sector = block*(PAGE_SIZE/512); + unsigned long data; + int rc; + + rc = __inode_direct_access(inode, sector, &data); + if (rc) + return rc; + clear_page((void*)data); + return 0; +} + +void ext2_xip_verify_sb(struct super_block *sb) +{ + struct ext2_sb_info *sbi = EXT2_SB(sb); + + if ((sbi->s_mount_opt & EXT2_MOUNT_XIP)) { + if ((sb->s_bdev == NULL) || + sb->s_bdev->bd_disk == NULL || + sb->s_bdev->bd_disk->fops == NULL || + sb->s_bdev->bd_disk->fops->direct_access == NULL) { + sbi->s_mount_opt &= (~EXT2_MOUNT_XIP); + ext2_warning(sb, __FUNCTION__, + "ignoring xip option - not supported by bdev"); + } + } +} + +struct page* +ext2_get_xip_page(struct address_space *mapping, sector_t blockno, + int create) +{ + int rc; + unsigned long data; + struct buffer_head tmp; + + tmp.b_state = 0; + tmp.b_blocknr = 0; + rc = ext2_get_block(mapping->host, blockno/(PAGE_SIZE/512) , &tmp, + create); + if (rc) + return ERR_PTR(rc); + if (tmp.b_blocknr == 0) { + /* SPARSE block */ + BUG_ON(create); + return ERR_PTR(-ENODATA); + } + + rc = __inode_direct_access + (mapping->host,tmp.b_blocknr*(PAGE_SIZE/512) ,&data); + if (rc) + return ERR_PTR(rc); + + SetPageUptodate(virt_to_page(data)); + return virt_to_page(data); +} diff --git a/fs/ext2/xip.h b/fs/ext2/xip.h new file mode 100644 index 000000000000..aa85331d6c56 --- /dev/null +++ b/fs/ext2/xip.h @@ -0,0 +1,25 @@ +/* + * linux/fs/ext2/xip.h + * + * Copyright (C) 2005 IBM Corporation + * Author: Carsten Otte (cotte@de.ibm.com) + */ + +#ifdef CONFIG_EXT2_FS_XIP +extern void ext2_xip_verify_sb (struct super_block *); +extern int ext2_clear_xip_target (struct inode *, int); + +static inline int ext2_use_xip (struct super_block *sb) +{ + struct ext2_sb_info *sbi = EXT2_SB(sb); + return (sbi->s_mount_opt & EXT2_MOUNT_XIP); +} +struct page* ext2_get_xip_page (struct address_space *, sector_t, int); +#define mapping_is_xip(map) unlikely(map->a_ops->get_xip_page) +#else +#define mapping_is_xip(map) 0 +#define ext2_xip_verify_sb(sb) do { } while (0) +#define ext2_use_xip(sb) 0 +#define ext2_clear_xip_target(inode, chain) 0 +#define ext2_get_xip_page NULL +#endif diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index fab43527e597..a657130ba03a 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -300,18 +300,19 @@ struct ext2_inode { /* * Mount flags */ -#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ -#define EXT2_MOUNT_OLDALLOC 0x0002 /* Don't use the new Orlov allocator */ -#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ -#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ -#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ -#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ -#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ -#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ -#define EXT2_MOUNT_NOBH 0x0100 /* No buffer_heads */ -#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ -#define EXT2_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */ -#define EXT2_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */ +#define EXT2_MOUNT_CHECK 0x000001 /* Do mount-time checks */ +#define EXT2_MOUNT_OLDALLOC 0x000002 /* Don't use the new Orlov allocator */ +#define EXT2_MOUNT_GRPID 0x000004 /* Create files with directory's group */ +#define EXT2_MOUNT_DEBUG 0x000008 /* Some debugging messages */ +#define EXT2_MOUNT_ERRORS_CONT 0x000010 /* Continue on errors */ +#define EXT2_MOUNT_ERRORS_RO 0x000020 /* Remount fs ro on errors */ +#define EXT2_MOUNT_ERRORS_PANIC 0x000040 /* Panic on errors */ +#define EXT2_MOUNT_MINIX_DF 0x000080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NOBH 0x000100 /* No buffer_heads */ +#define EXT2_MOUNT_NO_UID32 0x000200 /* Disable 32-bit UIDs */ +#define EXT2_MOUNT_XATTR_USER 0x004000 /* Extended user attributes */ +#define EXT2_MOUNT_POSIX_ACL 0x008000 /* POSIX Access Control Lists */ +#define EXT2_MOUNT_XIP 0x010000 /* Execute in place */ #define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt #define set_opt(o, opt) o |= EXT2_MOUNT_##opt diff --git a/include/linux/fs.h b/include/linux/fs.h index 79c0fafc0211..7e0501895f35 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1513,6 +1513,11 @@ extern ssize_t xip_file_aio_write(struct kiocb *iocb, const char __user *buf, extern ssize_t xip_file_writev(struct file *file, const struct iovec *iov, unsigned long nr_segs, loff_t *ppos); extern int xip_truncate_page(struct address_space *mapping, loff_t from); +#else +static inline int xip_truncate_page(struct address_space *mapping, loff_t from) +{ + return 0; +} #endif static inline void do_generic_file_read(struct file * filp, loff_t *ppos, -- cgit v1.2.3 From eb6fe0c388e43b02e261f0fdee60e42f6298d7f7 Mon Sep 17 00:00:00 2001 From: Carsten Otte Date: Thu, 23 Jun 2005 22:05:28 -0700 Subject: [PATCH] xip: reduce code duplication This patch reworks filemap_xip.c with the goal to reduce code duplication from mm/filemap.c. It applies agains 2.6.12-rc6-mm1. Instead of implementing the aio functions, this one implements the synchronous read/write functions only. For readv and writev, the generic fallback is used. For aio, we rely on the application doing the fallback. Since our "synchronous" function does memcpy immediately anyway, there is no performance difference between using the fallbacks or implementing each operation. Signed-off-by: Carsten Otte Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext2/file.c | 8 +- include/linux/fs.h | 12 +-- mm/filemap.h | 2 +- mm/filemap_xip.c | 246 ++++++++++++----------------------------------------- 4 files changed, 63 insertions(+), 205 deletions(-) (limited to 'include/linux/fs.h') diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 2b3d572365af..a484412fc782 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -58,17 +58,13 @@ struct file_operations ext2_file_operations = { #ifdef CONFIG_EXT2_FS_XIP struct file_operations ext2_xip_file_operations = { .llseek = generic_file_llseek, - .read = do_sync_read, - .write = do_sync_write, - .aio_read = xip_file_aio_read, - .aio_write = xip_file_aio_write, + .read = xip_file_read, + .write = xip_file_write, .ioctl = ext2_ioctl, .mmap = xip_file_mmap, .open = generic_file_open, .release = ext2_release_file, .fsync = ext2_sync_file, - .readv = xip_file_readv, - .writev = xip_file_writev, .sendfile = xip_file_sendfile, }; #endif diff --git a/include/linux/fs.h b/include/linux/fs.h index 7e0501895f35..3ae8e37bdfc8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1500,18 +1500,14 @@ extern int generic_file_open(struct inode * inode, struct file * filp); extern int nonseekable_open(struct inode * inode, struct file * filp); #ifdef CONFIG_FS_XIP -extern ssize_t xip_file_aio_read(struct kiocb *iocb, char __user *buf, - size_t count, loff_t pos); -extern ssize_t xip_file_readv(struct file *filp, const struct iovec *iov, - unsigned long nr_segs, loff_t *ppos); +extern ssize_t xip_file_read(struct file *filp, char __user *buf, size_t len, + loff_t *ppos); extern ssize_t xip_file_sendfile(struct file *in_file, loff_t *ppos, size_t count, read_actor_t actor, void *target); extern int xip_file_mmap(struct file * file, struct vm_area_struct * vma); -extern ssize_t xip_file_aio_write(struct kiocb *iocb, const char __user *buf, - size_t count, loff_t pos); -extern ssize_t xip_file_writev(struct file *file, const struct iovec *iov, - unsigned long nr_segs, loff_t *ppos); +extern ssize_t xip_file_write(struct file *filp, const char __user *buf, + size_t len, loff_t *ppos); extern int xip_truncate_page(struct address_space *mapping, loff_t from); #else static inline int xip_truncate_page(struct address_space *mapping, loff_t from) diff --git a/mm/filemap.h b/mm/filemap.h index c2d0546a57eb..13793ba0ce17 100644 --- a/mm/filemap.h +++ b/mm/filemap.h @@ -15,7 +15,7 @@ #include #include -extern size_t +size_t __filemap_copy_from_user_iovec(char *vaddr, const struct iovec *iov, size_t base, diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 7d63acd48817..3b6e384b98a6 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -114,83 +114,28 @@ out: file_accessed(filp); } -/* - * This is the "read()" routine for all filesystems - * that uses the get_xip_page address space operation. - */ -static ssize_t -__xip_file_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t *ppos) -{ - struct file *filp = iocb->ki_filp; - ssize_t retval; - unsigned long seg; - size_t count; - - count = 0; - for (seg = 0; seg < nr_segs; seg++) { - const struct iovec *iv = &iov[seg]; - - /* - * If any segment has a negative length, or the cumulative - * length ever wraps negative then return -EINVAL. - */ - count += iv->iov_len; - if (unlikely((ssize_t)(count|iv->iov_len) < 0)) - return -EINVAL; - if (access_ok(VERIFY_WRITE, iv->iov_base, iv->iov_len)) - continue; - if (seg == 0) - return -EFAULT; - nr_segs = seg; - count -= iv->iov_len; /* This segment is no good */ - break; - } - - retval = 0; - if (count) { - for (seg = 0; seg < nr_segs; seg++) { - read_descriptor_t desc; - - desc.written = 0; - desc.arg.buf = iov[seg].iov_base; - desc.count = iov[seg].iov_len; - if (desc.count == 0) - continue; - desc.error = 0; - do_xip_mapping_read(filp->f_mapping, &filp->f_ra, filp, - ppos, &desc, file_read_actor); - retval += desc.written; - if (!retval) { - retval = desc.error; - break; - } - } - } - return retval; -} - ssize_t -xip_file_aio_read(struct kiocb *iocb, char __user *buf, size_t count, - loff_t pos) +xip_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { - struct iovec local_iov = { .iov_base = buf, .iov_len = count }; + read_descriptor_t desc; - BUG_ON(iocb->ki_pos != pos); - return __xip_file_aio_read(iocb, &local_iov, 1, &iocb->ki_pos); -} -EXPORT_SYMBOL_GPL(xip_file_aio_read); + if (!access_ok(VERIFY_WRITE, buf, len)) + return -EFAULT; -ssize_t -xip_file_readv(struct file *filp, const struct iovec *iov, - unsigned long nr_segs, loff_t *ppos) -{ - struct kiocb kiocb; + desc.written = 0; + desc.arg.buf = buf; + desc.count = len; + desc.error = 0; - init_sync_kiocb(&kiocb, filp); - return __xip_file_aio_read(&kiocb, iov, nr_segs, ppos); + do_xip_mapping_read(filp->f_mapping, &filp->f_ra, filp, + ppos, &desc, file_read_actor); + + if (desc.written) + return desc.written; + else + return desc.error; } -EXPORT_SYMBOL_GPL(xip_file_readv); +EXPORT_SYMBOL_GPL(xip_file_read); ssize_t xip_file_sendfile(struct file *in_file, loff_t *ppos, @@ -326,25 +271,19 @@ int xip_file_mmap(struct file * file, struct vm_area_struct * vma) EXPORT_SYMBOL_GPL(xip_file_mmap); static ssize_t -do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos, loff_t *ppos, - size_t count) +__xip_file_write(struct file *filp, const char __user *buf, + size_t count, loff_t pos, loff_t *ppos) { - struct file *file = iocb->ki_filp; - struct address_space * mapping = file->f_mapping; + struct address_space * mapping = filp->f_mapping; struct address_space_operations *a_ops = mapping->a_ops; struct inode *inode = mapping->host; long status = 0; struct page *page; size_t bytes; - const struct iovec *cur_iov = iov; /* current iovec */ - size_t iov_base = 0; /* offset in the current iovec */ - char __user *buf; ssize_t written = 0; BUG_ON(!mapping->a_ops->get_xip_page); - buf = iov->iov_base; do { unsigned long index; unsigned long offset; @@ -365,15 +304,14 @@ do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, fault_in_pages_readable(buf, bytes); page = a_ops->get_xip_page(mapping, - index*(PAGE_SIZE/512), 0); + index*(PAGE_SIZE/512), 0); if (IS_ERR(page) && (PTR_ERR(page) == -ENODATA)) { /* we allocate a new page unmap it */ page = a_ops->get_xip_page(mapping, - index*(PAGE_SIZE/512), 1); + index*(PAGE_SIZE/512), 1); if (!IS_ERR(page)) - /* unmap page at pgoff from all other vmas */ - __xip_unmap(mapping, index); - + /* unmap page at pgoff from all other vmas */ + __xip_unmap(mapping, index); } if (IS_ERR(page)) { @@ -383,12 +321,7 @@ do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, BUG_ON(!PageUptodate(page)); - if (likely(nr_segs == 1)) - copied = filemap_copy_from_user(page, offset, - buf, bytes); - else - copied = filemap_copy_from_user_iovec(page, offset, - cur_iov, iov_base, bytes); + copied = filemap_copy_from_user(page, offset, buf, bytes); flush_dcache_page(page); if (likely(copied > 0)) { status = copied; @@ -398,9 +331,6 @@ do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, count -= status; pos += status; buf += status; - if (unlikely(nr_segs > 1)) - filemap_set_next_iovec(&cur_iov, - &iov_base, status); } } if (unlikely(copied != bytes)) @@ -422,110 +352,52 @@ do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, return written ? written : status; } -static ssize_t -xip_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t *ppos) +ssize_t +xip_file_write(struct file *filp, const char __user *buf, size_t len, + loff_t *ppos) { - struct file *file = iocb->ki_filp; - struct address_space * mapping = file->f_mapping; - size_t ocount; /* original count */ - size_t count; /* after file limit checks */ - struct inode *inode = mapping->host; - unsigned long seg; - loff_t pos; - ssize_t written; - ssize_t err; + struct address_space *mapping = filp->f_mapping; + struct inode *inode = mapping->host; + size_t count; + loff_t pos; + ssize_t ret; - ocount = 0; - for (seg = 0; seg < nr_segs; seg++) { - const struct iovec *iv = &iov[seg]; + down(&inode->i_sem); - /* - * If any segment has a negative length, or the cumulative - * length ever wraps negative then return -EINVAL. - */ - ocount += iv->iov_len; - if (unlikely((ssize_t)(ocount|iv->iov_len) < 0)) - return -EINVAL; - if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len)) - continue; - if (seg == 0) - return -EFAULT; - nr_segs = seg; - ocount -= iv->iov_len; /* This segment is no good */ - break; + if (!access_ok(VERIFY_READ, buf, len)) { + ret=-EFAULT; + goto out_up; } - count = ocount; pos = *ppos; + count = len; vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); - written = 0; - - err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); - if (err) - goto out; + /* We can write back this queue in page reclaim */ + current->backing_dev_info = mapping->backing_dev_info; + ret = generic_write_checks(filp, &pos, &count, S_ISBLK(inode->i_mode)); + if (ret) + goto out_backing; if (count == 0) - goto out; + goto out_backing; - err = remove_suid(file->f_dentry); - if (err) - goto out; + ret = remove_suid(filp->f_dentry); + if (ret) + goto out_backing; inode_update_time(inode, 1); - /* use execute in place to copy directly to disk */ - written = do_xip_file_write (iocb, iov, - nr_segs, pos, ppos, count); - out: - return written ? written : err; -} - -static ssize_t -__xip_file_write_nolock(struct file *file, const struct iovec *iov, - unsigned long nr_segs, loff_t *ppos) -{ - struct kiocb kiocb; - - init_sync_kiocb(&kiocb, file); - return xip_file_aio_write_nolock(&kiocb, iov, nr_segs, ppos); -} - -ssize_t -xip_file_aio_write(struct kiocb *iocb, const char __user *buf, - size_t count, loff_t pos) -{ - struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; - ssize_t ret; - struct iovec local_iov = { .iov_base = (void __user *)buf, - .iov_len = count }; + ret = __xip_file_write (filp, buf, count, pos, ppos); - BUG_ON(iocb->ki_pos != pos); - - down(&inode->i_sem); - ret = xip_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos); + out_backing: + current->backing_dev_info = NULL; + out_up: up(&inode->i_sem); return ret; } -EXPORT_SYMBOL_GPL(xip_file_aio_write); - -ssize_t xip_file_writev(struct file *file, const struct iovec *iov, - unsigned long nr_segs, loff_t *ppos) -{ - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; - ssize_t ret; - - down(&inode->i_sem); - ret = __xip_file_write_nolock(file, iov, nr_segs, ppos); - up(&inode->i_sem); - return ret; -} -EXPORT_SYMBOL_GPL(xip_file_writev); +EXPORT_SYMBOL_GPL(xip_file_write); /* * truncate a page used for execute in place @@ -541,7 +413,6 @@ xip_truncate_page(struct address_space *mapping, loff_t from) unsigned length; struct page *page; void *kaddr; - int err; BUG_ON(!mapping->a_ops->get_xip_page); @@ -556,17 +427,14 @@ xip_truncate_page(struct address_space *mapping, loff_t from) page = mapping->a_ops->get_xip_page(mapping, index*(PAGE_SIZE/512), 0); - err = -ENOMEM; if (!page) - goto out; + return -ENOMEM; if (unlikely(IS_ERR(page))) { - if (PTR_ERR(page) == -ENODATA) { + if (PTR_ERR(page) == -ENODATA) /* Hole? No need to truncate */ return 0; - } else { - err = PTR_ERR(page); - goto out; - } + else + return PTR_ERR(page); } else BUG_ON(!PageUptodate(page)); kaddr = kmap_atomic(page, KM_USER0); @@ -574,8 +442,6 @@ xip_truncate_page(struct address_space *mapping, loff_t from) kunmap_atomic(kaddr, KM_USER0); flush_dcache_page(page); - err = 0; -out: - return err; + return 0; } EXPORT_SYMBOL_GPL(xip_truncate_page); -- cgit v1.2.3