Commit 2796576e authored by Linus Torvalds's avatar Linus Torvalds

Merge home.transmeta.com:/home/torvalds/v2.5/morton

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 98d809e7 071c9b22
......@@ -195,11 +195,15 @@ static loff_t block_llseek(struct file *file, loff_t offset, int origin)
static int __block_fsync(struct inode * inode)
{
int ret;
filemap_fdatasync(inode->i_mapping);
ret = sync_buffers(inode->i_rdev, 1);
filemap_fdatawait(inode->i_mapping);
int ret, err;
ret = filemap_fdatasync(inode->i_mapping);
err = sync_buffers(inode->i_rdev, 1);
if (err && !ret)
ret = err;
err = filemap_fdatawait(inode->i_mapping);
if (err && !ret)
ret = err;
return ret;
}
......
......@@ -410,9 +410,9 @@ asmlinkage long sys_fsync(unsigned int fd)
struct file * file;
struct dentry * dentry;
struct inode * inode;
int err;
int ret, err;
err = -EBADF;
ret = -EBADF;
file = fget(fd);
if (!file)
goto out;
......@@ -420,21 +420,27 @@ asmlinkage long sys_fsync(unsigned int fd)
dentry = file->f_dentry;
inode = dentry->d_inode;
err = -EINVAL;
if (!file->f_op || !file->f_op->fsync)
ret = -EINVAL;
if (!file->f_op || !file->f_op->fsync) {
/* Why? We can still call filemap_fdatasync */
goto out_putf;
}
/* We need to protect against concurrent writers.. */
down(&inode->i_sem);
filemap_fdatasync(inode->i_mapping);
ret = filemap_fdatasync(inode->i_mapping);
err = file->f_op->fsync(file, dentry, 0);
filemap_fdatawait(inode->i_mapping);
if (err && !ret)
ret = err;
err = filemap_fdatawait(inode->i_mapping);
if (err && !ret)
ret = err;
up(&inode->i_sem);
out_putf:
fput(file);
out:
return err;
return ret;
}
asmlinkage long sys_fdatasync(unsigned int fd)
......@@ -442,9 +448,9 @@ asmlinkage long sys_fdatasync(unsigned int fd)
struct file * file;
struct dentry * dentry;
struct inode * inode;
int err;
int ret, err;
err = -EBADF;
ret = -EBADF;
file = fget(fd);
if (!file)
goto out;
......@@ -452,20 +458,24 @@ asmlinkage long sys_fdatasync(unsigned int fd)
dentry = file->f_dentry;
inode = dentry->d_inode;
err = -EINVAL;
ret = -EINVAL;
if (!file->f_op || !file->f_op->fsync)
goto out_putf;
down(&inode->i_sem);
filemap_fdatasync(inode->i_mapping);
ret = filemap_fdatasync(inode->i_mapping);
err = file->f_op->fsync(file, dentry, 1);
filemap_fdatawait(inode->i_mapping);
if (err && !ret)
ret = err;
err = filemap_fdatawait(inode->i_mapping);
if (err && !ret)
ret = err;
up(&inode->i_sem);
out_putf:
fput(file);
out:
return err;
return ret;
}
/* After several hours of tedious analysis, the following hash
......@@ -1431,6 +1441,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, get_b
int err, i;
unsigned long block;
struct buffer_head *bh, *head;
int need_unlock;
if (!PageLocked(page))
BUG();
......@@ -1486,8 +1497,34 @@ static int __block_write_full_page(struct inode *inode, struct page *page, get_b
return 0;
out:
/*
* ENOSPC, or some other error. We may already have added some
* blocks to the file, so we need to write these out to avoid
* exposing stale data.
*/
ClearPageUptodate(page);
UnlockPage(page);
bh = head;
need_unlock = 1;
/* Recovery: lock and submit the mapped buffers */
do {
if (buffer_mapped(bh)) {
lock_buffer(bh);
set_buffer_async_io(bh);
need_unlock = 0;
}
bh = bh->b_this_page;
} while (bh != head);
do {
struct buffer_head *next = bh->b_this_page;
if (buffer_mapped(bh)) {
set_bit(BH_Uptodate, &bh->b_state);
clear_bit(BH_Dirty, &bh->b_state);
submit_bh(WRITE, bh);
}
bh = next;
} while (bh != head);
if (need_unlock)
UnlockPage(page);
return err;
}
......@@ -1518,6 +1555,7 @@ static int __block_prepare_write(struct inode *inode, struct page *page,
continue;
if (block_start >= to)
break;
clear_bit(BH_New, &bh->b_state);
if (!buffer_mapped(bh)) {
err = get_block(inode, block, bh, 1);
if (err)
......@@ -1552,12 +1590,35 @@ static int __block_prepare_write(struct inode *inode, struct page *page,
*/
while(wait_bh > wait) {
wait_on_buffer(*--wait_bh);
err = -EIO;
if (!buffer_uptodate(*wait_bh))
goto out;
return -EIO;
}
return 0;
out:
/*
* Zero out any newly allocated blocks to avoid exposing stale
* data. If BH_New is set, we know that the block was newly
* allocated in the above loop.
*/
bh = head;
block_start = 0;
do {
block_end = block_start+blocksize;
if (block_end <= from)
goto next_bh;
if (block_start >= to)
break;
if (buffer_new(bh)) {
if (buffer_uptodate(bh))
printk(KERN_ERR "%s: zeroing uptodate buffer!\n", __FUNCTION__);
memset(kaddr+block_start, 0, bh->b_size);
set_bit(BH_Uptodate, &bh->b_state);
mark_buffer_dirty(bh);
}
next_bh:
block_start = block_end;
bh = bh->b_this_page;
} while (bh != head);
return err;
}
......@@ -1954,6 +2015,48 @@ int block_write_full_page(struct page *page, get_block_t *get_block)
goto done;
}
/*
* Commence writeout of all the buffers against a page. The
* page must be locked. Returns zero on success or a negative
* errno.
*/
int writeout_one_page(struct page *page)
{
struct buffer_head *bh, *head = page->buffers;
if (!PageLocked(page))
BUG();
bh = head;
do {
if (buffer_locked(bh) || !buffer_dirty(bh) || !buffer_uptodate(bh))
continue;
bh->b_flushtime = jiffies;
ll_rw_block(WRITE, 1, &bh);
} while ((bh = bh->b_this_page) != head);
return 0;
}
EXPORT_SYMBOL(writeout_one_page);
/*
* Wait for completion of I/O of all buffers against a page. The page
* must be locked. Returns zero on success or a negative errno.
*/
int waitfor_one_page(struct page *page)
{
int error = 0;
struct buffer_head *bh, *head = page->buffers;
bh = head;
do {
wait_on_buffer(bh);
if (buffer_req(bh) && !buffer_uptodate(bh))
error = -EIO;
} while ((bh = bh->b_this_page) != head);
return error;
}
EXPORT_SYMBOL(waitfor_one_page);
sector_t generic_block_bmap(struct address_space *mapping, sector_t block,
get_block_t *get_block)
{
......
......@@ -52,8 +52,13 @@ static int ext2_commit_chunk(struct page *page, unsigned from, unsigned to)
int err = 0;
dir->i_version = ++event;
page->mapping->a_ops->commit_write(NULL, page, from, to);
if (IS_SYNC(dir))
err = waitfor_one_page(page);
if (IS_SYNC(dir)) {
int err2;
err = writeout_one_page(page);
err2 = waitfor_one_page(page);
if (err == 0)
err = err2;
}
return err;
}
......
......@@ -36,8 +36,13 @@ static int dir_commit_chunk(struct page *page, unsigned from, unsigned to)
struct inode *dir = (struct inode *)page->mapping->host;
int err = 0;
page->mapping->a_ops->commit_write(NULL, page, from, to);
if (IS_SYNC(dir))
err = waitfor_one_page(page);
if (IS_SYNC(dir)) {
int err2;
err = writeout_one_page(page);
err2 = waitfor_one_page(page);
if (err == 0)
err = err2;
}
return err;
}
......@@ -236,10 +241,10 @@ int minix_delete_entry(struct minix_dir_entry *de, struct page *page)
lock_page(page);
err = mapping->a_ops->prepare_write(NULL, page, from, to);
if (err)
BUG();
de->inode = 0;
err = dir_commit_chunk(page, from, to);
if (err == 0) {
de->inode = 0;
err = dir_commit_chunk(page, from, to);
}
UnlockPage(page);
dir_put_page(page);
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
......@@ -336,10 +341,10 @@ void minix_set_link(struct minix_dir_entry *de, struct page *page,
lock_page(page);
err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
if (err)
BUG();
de->inode = inode->i_ino;
err = dir_commit_chunk(page, from, to);
if (err == 0) {
de->inode = inode->i_ino;
err = dir_commit_chunk(page, from, to);
}
UnlockPage(page);
dir_put_page(page);
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
......
......@@ -244,6 +244,7 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
{
struct inode * inode = filp->f_dentry->d_inode;
int status = 0;
int status2;
dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
inode->i_sb->s_id, inode->i_ino,
......@@ -278,11 +279,15 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
* Flush all pending writes before doing anything
* with locks..
*/
filemap_fdatasync(inode->i_mapping);
status = filemap_fdatasync(inode->i_mapping);
down(&inode->i_sem);
status = nfs_wb_all(inode);
status2 = nfs_wb_all(inode);
if (status2 && !status)
status = status2;
up(&inode->i_sem);
filemap_fdatawait(inode->i_mapping);
status2 = filemap_fdatawait(inode->i_mapping);
if (status2 && !status)
status = status2;
if (status < 0)
return status;
......
Thu Feb 14 2002 Andrew Morton <akpm@zip.com.au>
* dir_commit_chunk(): call writeout_one_page() as well as
waitfor_one_page() for IS_SYNC directories, so that we
actually do sync the directory. (forward-port from 2.4).
Thu Feb 7 2002 Alexander Viro <viro@math.psu.edu>
* super.c: switched to ->get_sb()
......@@ -97,3 +103,4 @@ Fri Oct 26 2001 Christoph Hellwig <hch@caldera.de>
Remove symlink faking. Noone really wants to use these as
linux filesystems and native OSes don't support it anyway.
......@@ -42,8 +42,13 @@ static int dir_commit_chunk(struct page *page, unsigned from, unsigned to)
int err = 0;
page->mapping->a_ops->commit_write(NULL, page, from, to);
if (IS_SYNC(dir))
err = waitfor_one_page(page);
if (IS_SYNC(dir)) {
int err2;
err = writeout_one_page(page);
err2 = waitfor_one_page(page);
if (err == 0)
err = err2;
}
return err;
}
......
......@@ -1230,8 +1230,8 @@ static inline int fsync_inode_data_buffers(struct inode *inode)
return fsync_buffers_list(&inode->i_dirty_data_buffers);
}
extern int inode_has_buffers(struct inode *);
extern void filemap_fdatasync(struct address_space *);
extern void filemap_fdatawait(struct address_space *);
extern int filemap_fdatasync(struct address_space *);
extern int filemap_fdatawait(struct address_space *);
extern void sync_supers(kdev_t);
extern int bmap(struct inode *, int);
extern int notify_change(struct dentry *, struct iattr *);
......@@ -1440,8 +1440,9 @@ sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *);
int generic_commit_write(struct file *, struct page *, unsigned, unsigned);
int block_truncate_page(struct address_space *, loff_t, get_block_t *);
extern int generic_direct_IO(int, struct inode *, struct kiobuf *, unsigned long, int, get_block_t *);
extern int waitfor_one_page(struct page *);
extern int writeout_one_page(struct page *);
extern int waitfor_one_page(struct page*);
extern int generic_file_mmap(struct file *, struct vm_area_struct *);
extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
......
......@@ -213,7 +213,6 @@ EXPORT_SYMBOL(cont_prepare_write);
EXPORT_SYMBOL(generic_commit_write);
EXPORT_SYMBOL(block_truncate_page);
EXPORT_SYMBOL(generic_block_bmap);
EXPORT_SYMBOL(waitfor_one_page);
EXPORT_SYMBOL(generic_file_read);
EXPORT_SYMBOL(do_generic_file_read);
EXPORT_SYMBOL(generic_file_write);
......
......@@ -449,41 +449,6 @@ static inline struct page * __find_page_nolock(struct address_space *mapping, un
return page;
}
/*
* By the time this is called, the page is locked and
* we don't have to worry about any races any more.
*
* Start the IO..
*/
static int writeout_one_page(struct page *page)
{
struct buffer_head *bh, *head = page->buffers;
bh = head;
do {
if (buffer_locked(bh) || !buffer_dirty(bh) || !buffer_uptodate(bh))
continue;
bh->b_flushtime = jiffies;
ll_rw_block(WRITE, 1, &bh);
} while ((bh = bh->b_this_page) != head);
return 0;
}
int waitfor_one_page(struct page *page)
{
int error = 0;
struct buffer_head *bh, *head = page->buffers;
bh = head;
do {
wait_on_buffer(bh);
if (buffer_req(bh) && !buffer_uptodate(bh))
error = -EIO;
} while ((bh = bh->b_this_page) != head);
return error;
}
static int do_buffer_fdatasync(struct list_head *head, unsigned long start, unsigned long end, int (*fn)(struct page *))
{
struct list_head *curr;
......@@ -577,8 +542,9 @@ EXPORT_SYMBOL(fail_writepage);
* @mapping: address space structure to write
*
*/
void filemap_fdatasync(struct address_space * mapping)
int filemap_fdatasync(struct address_space * mapping)
{
int ret = 0;
int (*writepage)(struct page *) = mapping->a_ops->writepage;
spin_lock(&pagecache_lock);
......@@ -598,8 +564,11 @@ void filemap_fdatasync(struct address_space * mapping)
lock_page(page);
if (PageDirty(page)) {
int err;
ClearPageDirty(page);
writepage(page);
err = writepage(page);
if (err && !ret)
ret = err;
} else
UnlockPage(page);
......@@ -607,6 +576,7 @@ void filemap_fdatasync(struct address_space * mapping)
spin_lock(&pagecache_lock);
}
spin_unlock(&pagecache_lock);
return ret;
}
/**
......@@ -616,8 +586,10 @@ void filemap_fdatasync(struct address_space * mapping)
* @mapping: address space structure to wait for
*
*/
void filemap_fdatawait(struct address_space * mapping)
int filemap_fdatawait(struct address_space * mapping)
{
int ret = 0;
spin_lock(&pagecache_lock);
while (!list_empty(&mapping->locked_pages)) {
......@@ -633,11 +605,14 @@ void filemap_fdatawait(struct address_space * mapping)
spin_unlock(&pagecache_lock);
___wait_on_page(page);
if (PageError(page))
ret = -EIO;
page_cache_release(page);
spin_lock(&pagecache_lock);
}
spin_unlock(&pagecache_lock);
return ret;
}
/*
......@@ -1514,12 +1489,14 @@ static ssize_t generic_file_direct_IO(int rw, struct file * filp, char * buf, si
goto out_free;
/*
* Flush to disk exlusively the _data_, metadata must remains
* Flush to disk exclusively the _data_, metadata must remain
* completly asynchronous or performance will go to /dev/null.
*/
filemap_fdatasync(mapping);
retval = fsync_inode_data_buffers(inode);
filemap_fdatawait(mapping);
retval = filemap_fdatasync(mapping);
if (retval == 0)
retval = fsync_inode_data_buffers(inode);
if (retval == 0)
retval = filemap_fdatawait(mapping);
if (retval < 0)
goto out_free;
......@@ -2136,26 +2113,45 @@ int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
* The msync() system call.
*/
/*
* MS_SYNC syncs the entire file - including mappings.
*
* MS_ASYNC initiates writeout of just the dirty mapped data.
* This provides no guarantee of file integrity - things like indirect
* blocks may not have started writeout. MS_ASYNC is primarily useful
* where the application knows that it has finished with the data and
* wishes to intelligently schedule its own I/O traffic.
*/
static int msync_interval(struct vm_area_struct * vma,
unsigned long start, unsigned long end, int flags)
{
int ret = 0;
struct file * file = vma->vm_file;
if (file && (vma->vm_flags & VM_SHARED)) {
int error;
error = filemap_sync(vma, start, end-start, flags);
ret = filemap_sync(vma, start, end-start, flags);
if (!error && (flags & MS_SYNC)) {
if (!ret && (flags & (MS_SYNC|MS_ASYNC))) {
struct inode * inode = file->f_dentry->d_inode;
down(&inode->i_sem);
filemap_fdatasync(inode->i_mapping);
if (file->f_op && file->f_op->fsync)
error = file->f_op->fsync(file, file->f_dentry, 1);
filemap_fdatawait(inode->i_mapping);
ret = filemap_fdatasync(inode->i_mapping);
if (flags & MS_SYNC) {
int err;
if (file->f_op && file->f_op->fsync) {
err = file->f_op->fsync(file, file->f_dentry, 1);
if (err && !ret)
ret = err;
}
err = filemap_fdatawait(inode->i_mapping);
if (err && !ret)
ret = err;
}
up(&inode->i_sem);
}
return error;
}
return 0;
return ret;
}
asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
......@@ -2999,7 +2995,7 @@ generic_file_write(struct file *file,const char *buf,size_t count, loff_t *ppos)
kaddr = kmap(page);
status = mapping->a_ops->prepare_write(file, page, offset, offset+bytes);
if (status)
goto unlock;
goto sync_failure;
page_fault = __copy_from_user(kaddr+offset, buf, bytes);
flush_dcache_page(page);
status = mapping->a_ops->commit_write(file, page, offset, offset+bytes);
......@@ -3024,6 +3020,7 @@ generic_file_write(struct file *file,const char *buf,size_t count, loff_t *ppos)
if (status < 0)
break;
} while (count);
done:
*ppos = pos;
if (cached_page)
......@@ -3046,6 +3043,18 @@ generic_file_write(struct file *file,const char *buf,size_t count, loff_t *ppos)
status = -EFAULT;
goto unlock;
sync_failure:
/*
* If blocksize < pagesize, prepare_write() may have instantiated a
* few blocks outside i_size. Trim these off again.
*/
kunmap(page);
UnlockPage(page);
page_cache_release(page);
if (pos + bytes > inode->i_size)
vmtruncate(inode, inode->i_size);
goto done;
o_direct:
written = generic_file_direct_IO(WRITE, file, (char *) buf, count, pos);
if (written > 0) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment