summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/buffer.c6
-rw-r--r--fs/direct-io.c21
-rw-r--r--fs/ext2/dir.c28
-rw-r--r--fs/ext3/inode.c17
-rw-r--r--fs/ext3/namei.c3
-rw-r--r--fs/jffs2/nodelist.c3
-rw-r--r--fs/jffs2/readinode.c2
-rw-r--r--fs/jfs/jfs_dmap.c7
-rw-r--r--fs/jfs/jfs_imap.c6
-rw-r--r--fs/lockd/clntproc.c9
-rw-r--r--fs/namei.c13
-rw-r--r--fs/namespace.c5
-rw-r--r--fs/nfs/direct.c10
-rw-r--r--fs/nfs/nfs4proc.c2
14 files changed, 76 insertions, 56 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index 62cfd17..a9b3994 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -3060,6 +3060,7 @@ int buffer_migrate_page(struct page *newpage, struct page *page)
{
struct address_space *mapping = page->mapping;
struct buffer_head *bh, *head;
+ int rc;
if (!mapping)
return -EAGAIN;
@@ -3069,8 +3070,9 @@ int buffer_migrate_page(struct page *newpage, struct page *page)
head = page_buffers(page);
- if (migrate_page_remove_references(newpage, page, 3))
- return -EAGAIN;
+ rc = migrate_page_remove_references(newpage, page, 3);
+ if (rc)
+ return rc;
bh = head;
do {
diff --git a/fs/direct-io.c b/fs/direct-io.c
index 848044a..27f3e78 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -1155,15 +1155,16 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode,
* For writes, i_mutex is not held on entry; it is never taken.
*
* DIO_LOCKING (simple locking for regular files)
- * For writes we are called under i_mutex and return with i_mutex held, even though
- * it is internally dropped.
+ * For writes we are called under i_mutex and return with i_mutex held, even
+ * though it is internally dropped.
* For reads, i_mutex is not held on entry, but it is taken and dropped before
* returning.
*
* DIO_OWN_LOCKING (filesystem provides synchronisation and handling of
* uninitialised data, allowing parallel direct readers and writers)
* For writes we are called without i_mutex, return without it, never touch it.
- * For reads, i_mutex is held on entry and will be released before returning.
+ * For reads we are called under i_mutex and return with i_mutex held, even
+ * though it may be internally dropped.
*
* Additional i_alloc_sem locking requirements described inline below.
*/
@@ -1182,7 +1183,8 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
ssize_t retval = -EINVAL;
loff_t end = offset;
struct dio *dio;
- int reader_with_isem = (rw == READ && dio_lock_type == DIO_OWN_LOCKING);
+ int release_i_mutex = 0;
+ int acquire_i_mutex = 0;
if (rw & WRITE)
current->flags |= PF_SYNCWRITE;
@@ -1225,7 +1227,6 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
* writers need to grab i_alloc_sem only (i_mutex is already held)
* For regular files using DIO_OWN_LOCKING,
* neither readers nor writers take any locks here
- * (i_mutex is already held and release for writers here)
*/
dio->lock_type = dio_lock_type;
if (dio_lock_type != DIO_NO_LOCKING) {
@@ -1236,7 +1237,7 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
mapping = iocb->ki_filp->f_mapping;
if (dio_lock_type != DIO_OWN_LOCKING) {
mutex_lock(&inode->i_mutex);
- reader_with_isem = 1;
+ release_i_mutex = 1;
}
retval = filemap_write_and_wait_range(mapping, offset,
@@ -1248,7 +1249,7 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
if (dio_lock_type == DIO_OWN_LOCKING) {
mutex_unlock(&inode->i_mutex);
- reader_with_isem = 0;
+ acquire_i_mutex = 1;
}
}
@@ -1269,11 +1270,13 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
nr_segs, blkbits, get_blocks, end_io, dio);
if (rw == READ && dio_lock_type == DIO_LOCKING)
- reader_with_isem = 0;
+ release_i_mutex = 0;
out:
- if (reader_with_isem)
+ if (release_i_mutex)
mutex_unlock(&inode->i_mutex);
+ else if (acquire_i_mutex)
+ mutex_lock(&inode->i_mutex);
if (rw & WRITE)
current->flags &= ~PF_SYNCWRITE;
return retval;
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index 7442bdd..b3dbd71 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -256,11 +256,10 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
unsigned long npages = dir_pages(inode);
unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);
unsigned char *types = NULL;
- int need_revalidate = (filp->f_version != inode->i_version);
- int ret;
+ int need_revalidate = filp->f_version != inode->i_version;
if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
- goto success;
+ return 0;
if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
types = ext2_filetype_table;
@@ -275,12 +274,15 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
"bad page in #%lu",
inode->i_ino);
filp->f_pos += PAGE_CACHE_SIZE - offset;
- ret = -EIO;
- goto done;
+ return -EIO;
}
kaddr = page_address(page);
- if (need_revalidate) {
- offset = ext2_validate_entry(kaddr, offset, chunk_mask);
+ if (unlikely(need_revalidate)) {
+ if (offset) {
+ offset = ext2_validate_entry(kaddr, offset, chunk_mask);
+ filp->f_pos = (n<<PAGE_CACHE_SHIFT) + offset;
+ }
+ filp->f_version = inode->i_version;
need_revalidate = 0;
}
de = (ext2_dirent *)(kaddr+offset);
@@ -289,9 +291,8 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
if (de->rec_len == 0) {
ext2_error(sb, __FUNCTION__,
"zero-length directory entry");
- ret = -EIO;
ext2_put_page(page);
- goto done;
+ return -EIO;
}
if (de->inode) {
int over;
@@ -306,19 +307,14 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
le32_to_cpu(de->inode), d_type);
if (over) {
ext2_put_page(page);
- goto success;
+ return 0;
}
}
filp->f_pos += le16_to_cpu(de->rec_len);
}
ext2_put_page(page);
}
-
-success:
- ret = 0;
-done:
- filp->f_version = inode->i_version;
- return ret;
+ return 0;
}
/*
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 3fc4238..0384e53 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -1624,15 +1624,14 @@ static int ext3_block_truncate_page(handle_t *handle, struct page *page,
* For "nobh" option, we can only work if we don't need to
* read-in the page - otherwise we create buffers to do the IO.
*/
- if (!page_has_buffers(page) && test_opt(inode->i_sb, NOBH)) {
- if (PageUptodate(page)) {
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + offset, 0, length);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
- set_page_dirty(page);
- goto unlock;
- }
+ if (!page_has_buffers(page) && test_opt(inode->i_sb, NOBH) &&
+ ext3_should_writeback_data(inode) && PageUptodate(page)) {
+ kaddr = kmap_atomic(page, KM_USER0);
+ memset(kaddr + offset, 0, length);
+ flush_dcache_page(page);
+ kunmap_atomic(kaddr, KM_USER0);
+ set_page_dirty(page);
+ goto unlock;
}
if (!page_has_buffers(page))
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
index 8bd8ac0..b8f5cd1 100644
--- a/fs/ext3/namei.c
+++ b/fs/ext3/namei.c
@@ -2141,7 +2141,8 @@ retry:
* We have a transaction open. All is sweetness. It also sets
* i_size in generic_commit_write().
*/
- err = page_symlink(inode, symname, l);
+ err = __page_symlink(inode, symname, l,
+ mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS);
if (err) {
ext3_dec_count(handle, inode);
ext3_mark_inode_dirty(handle, inode);
diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c
index b635e16..d4d0c41 100644
--- a/fs/jffs2/nodelist.c
+++ b/fs/jffs2/nodelist.c
@@ -406,7 +406,8 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
int err = 0, pointed = 0;
struct jffs2_eraseblock *jeb;
unsigned char *buffer;
- uint32_t crc, ofs, retlen, len;
+ uint32_t crc, ofs, len;
+ size_t retlen;
BUG_ON(tn->csize == 0);
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index 5f0652d..f169564 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -112,7 +112,7 @@ static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_r
* negative error code on failure.
*/
static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
- struct jffs2_raw_dirent *rd, uint32_t read, struct jffs2_full_dirent **fdp,
+ struct jffs2_raw_dirent *rd, size_t read, struct jffs2_full_dirent **fdp,
uint32_t *latest_mctime, uint32_t *mctime_ver)
{
struct jffs2_full_dirent *fd;
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index 2967b73..79b5404 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -532,10 +532,10 @@ dbUpdatePMap(struct inode *ipbmap,
lastlblkno = lblkno;
+ LOGSYNC_LOCK(log, flags);
if (mp->lsn != 0) {
/* inherit older/smaller lsn */
logdiff(diffp, mp->lsn, log);
- LOGSYNC_LOCK(log, flags);
if (difft < diffp) {
mp->lsn = lsn;
@@ -548,20 +548,17 @@ dbUpdatePMap(struct inode *ipbmap,
logdiff(diffp, mp->clsn, log);
if (difft > diffp)
mp->clsn = tblk->clsn;
- LOGSYNC_UNLOCK(log, flags);
} else {
mp->log = log;
mp->lsn = lsn;
/* insert bp after tblock in logsync list */
- LOGSYNC_LOCK(log, flags);
-
log->count++;
list_add(&mp->synclist, &tblk->synclist);
mp->clsn = tblk->clsn;
- LOGSYNC_UNLOCK(log, flags);
}
+ LOGSYNC_UNLOCK(log, flags);
}
/* write the last buffer. */
diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c
index 31b4aa1..4efa0d0 100644
--- a/fs/jfs/jfs_imap.c
+++ b/fs/jfs/jfs_imap.c
@@ -2844,11 +2844,11 @@ diUpdatePMap(struct inode *ipimap,
*/
lsn = tblk->lsn;
log = JFS_SBI(tblk->sb)->log;
+ LOGSYNC_LOCK(log, flags);
if (mp->lsn != 0) {
/* inherit older/smaller lsn */
logdiff(difft, lsn, log);
logdiff(diffp, mp->lsn, log);
- LOGSYNC_LOCK(log, flags);
if (difft < diffp) {
mp->lsn = lsn;
/* move mp after tblock in logsync list */
@@ -2860,17 +2860,15 @@ diUpdatePMap(struct inode *ipimap,
logdiff(diffp, mp->clsn, log);
if (difft > diffp)
mp->clsn = tblk->clsn;
- LOGSYNC_UNLOCK(log, flags);
} else {
mp->log = log;
mp->lsn = lsn;
/* insert mp after tblock in logsync list */
- LOGSYNC_LOCK(log, flags);
log->count++;
list_add(&mp->synclist, &tblk->synclist);
mp->clsn = tblk->clsn;
- LOGSYNC_UNLOCK(log, flags);
}
+ LOGSYNC_UNLOCK(log, flags);
write_metapage(mp);
return (0);
}
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index 220058d..970b6a6 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -662,12 +662,18 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
* reclaimed while we're stuck in the unlock call. */
fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED;
+ /*
+ * Note: the server is supposed to either grant us the unlock
+ * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either
+ * case, we want to unlock.
+ */
+ do_vfs_lock(fl);
+
if (req->a_flags & RPC_TASK_ASYNC) {
status = nlmclnt_async_call(req, NLMPROC_UNLOCK,
&nlmclnt_unlock_ops);
/* Hrmf... Do the unlock early since locks_remove_posix()
* really expects us to free the lock synchronously */
- do_vfs_lock(fl);
if (status < 0) {
nlmclnt_release_lockargs(req);
kfree(req);
@@ -680,7 +686,6 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
if (status < 0)
return status;
- do_vfs_lock(fl);
if (resp->status == NLM_LCK_GRANTED)
return 0;
diff --git a/fs/namei.c b/fs/namei.c
index 557dcf3..8dc2b03 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2613,13 +2613,15 @@ void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
}
}
-int page_symlink(struct inode *inode, const char *symname, int len)
+int __page_symlink(struct inode *inode, const char *symname, int len,
+ gfp_t gfp_mask)
{
struct address_space *mapping = inode->i_mapping;
- struct page *page = grab_cache_page(mapping, 0);
+ struct page *page;
int err = -ENOMEM;
char *kaddr;
+ page = find_or_create_page(mapping, 0, gfp_mask);
if (!page)
goto fail;
err = mapping->a_ops->prepare_write(NULL, page, 0, len-1);
@@ -2654,6 +2656,12 @@ fail:
return err;
}
+int page_symlink(struct inode *inode, const char *symname, int len)
+{
+ return __page_symlink(inode, symname, len,
+ mapping_gfp_mask(inode->i_mapping));
+}
+
struct inode_operations page_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = page_follow_link_light,
@@ -2672,6 +2680,7 @@ EXPORT_SYMBOL(lookup_one_len);
EXPORT_SYMBOL(page_follow_link_light);
EXPORT_SYMBOL(page_put_link);
EXPORT_SYMBOL(page_readlink);
+EXPORT_SYMBOL(__page_symlink);
EXPORT_SYMBOL(page_symlink);
EXPORT_SYMBOL(page_symlink_inode_operations);
EXPORT_SYMBOL(path_lookup);
diff --git a/fs/namespace.c b/fs/namespace.c
index 058a448..39c81a8 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1338,7 +1338,7 @@ struct namespace *dup_namespace(struct task_struct *tsk, struct fs_struct *fs)
new_ns = kmalloc(sizeof(struct namespace), GFP_KERNEL);
if (!new_ns)
- goto out;
+ return NULL;
atomic_set(&new_ns->count, 1);
INIT_LIST_HEAD(&new_ns->list);
@@ -1352,7 +1352,7 @@ struct namespace *dup_namespace(struct task_struct *tsk, struct fs_struct *fs)
if (!new_ns->root) {
up_write(&namespace_sem);
kfree(new_ns);
- goto out;
+ return NULL;
}
spin_lock(&vfsmount_lock);
list_add_tail(&new_ns->list, &new_ns->root->mnt_list);
@@ -1393,7 +1393,6 @@ struct namespace *dup_namespace(struct task_struct *tsk, struct fs_struct *fs)
if (altrootmnt)
mntput(altrootmnt);
-out:
return new_ns;
}
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 04ab2fc..4e9b3a1 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -57,6 +57,7 @@
#define NFSDBG_FACILITY NFSDBG_VFS
#define MAX_DIRECTIO_SIZE (4096UL << PAGE_SHIFT)
+static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty);
static kmem_cache_t *nfs_direct_cachep;
/*
@@ -107,6 +108,15 @@ nfs_get_user_pages(int rw, unsigned long user_addr, size_t size,
page_count, (rw == READ), 0,
*pages, NULL);
up_read(&current->mm->mmap_sem);
+ /*
+ * If we got fewer pages than expected from get_user_pages(),
+ * the user buffer runs off the end of a mapping; return EFAULT.
+ */
+ if (result >= 0 && result < page_count) {
+ nfs_free_user_pages(*pages, result, 0);
+ *pages = NULL;
+ result = -EFAULT;
+ }
}
return result;
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 984ca34..f8c0066 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1430,7 +1430,7 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
if (status == 0)
status = nfs4_do_fsinfo(server, fhandle, info);
out:
- return status;
+ return nfs4_map_errors(status);
}
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)