2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* linux/mm/filemap.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1994-1999 Linus Torvalds
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file handles the generic file mmap semantics used by
|
|
|
|
* most "normal" filesystems (but you don't /have/ to use this:
|
|
|
|
* the NFS filesystem used to do this differently, for example)
|
|
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/compiler.h>
|
|
|
|
#include <linux/fs.h>
|
2006-06-23 17:04:16 +08:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/aio.h>
|
2006-01-12 04:17:46 +08:00
|
|
|
#include <linux/capability.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/kernel_stat.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/swap.h>
|
|
|
|
#include <linux/mman.h>
|
|
|
|
#include <linux/pagemap.h>
|
|
|
|
#include <linux/file.h>
|
|
|
|
#include <linux/uio.h>
|
|
|
|
#include <linux/hash.h>
|
|
|
|
#include <linux/writeback.h>
|
|
|
|
#include <linux/pagevec.h>
|
|
|
|
#include <linux/blkdev.h>
|
|
|
|
#include <linux/security.h>
|
|
|
|
#include <linux/syscalls.h>
|
2006-03-24 19:16:04 +08:00
|
|
|
#include <linux/cpuset.h>
|
2005-06-24 13:05:25 +08:00
|
|
|
#include "filemap.h"
|
2006-03-22 16:08:33 +08:00
|
|
|
#include "internal.h"
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* FIXME: remove all knowledge of the buffer layer from the core VM
|
|
|
|
*/
|
|
|
|
#include <linux/buffer_head.h> /* for generic_osync_inode */
|
|
|
|
|
|
|
|
#include <asm/mman.h>
|
|
|
|
|
2005-09-10 15:26:28 +08:00
|
|
|
static ssize_t
|
|
|
|
generic_file_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
|
|
|
|
loff_t offset, unsigned long nr_segs);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Shared mappings implemented 30.11.1994. It's not fully working yet,
|
|
|
|
* though.
|
|
|
|
*
|
|
|
|
* Shared mappings now work. 15.8.1995 Bruno.
|
|
|
|
*
|
|
|
|
* finished 'unifying' the page and buffer cache and SMP-threaded the
|
|
|
|
* page-cache, 21.05.1999, Ingo Molnar <mingo@redhat.com>
|
|
|
|
*
|
|
|
|
* SMP-threaded pagemap-LRU 1999, Andrea Arcangeli <andrea@suse.de>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lock ordering:
|
|
|
|
*
|
|
|
|
* ->i_mmap_lock (vmtruncate)
|
|
|
|
* ->private_lock (__free_pte->__set_page_dirty_buffers)
|
[PATCH] swap: swap_lock replace list+device
The idea of a swap_device_lock per device, and a swap_list_lock over them all,
is appealing; but in practice almost every holder of swap_device_lock must
already hold swap_list_lock, which defeats the purpose of the split.
The only exceptions have been swap_duplicate, valid_swaphandles and an
untrodden path in try_to_unuse (plus a few places added in this series).
valid_swaphandles doesn't show up high in profiles, but swap_duplicate does
demand attention. However, with the hold time in get_swap_pages so much
reduced, I've not yet found a load and set of swap device priorities to show
even swap_duplicate benefitting from the split. Certainly the split is mere
overhead in the common case of a single swap device.
So, replace swap_list_lock and swap_device_lock by spinlock_t swap_lock
(generally we seem to prefer an _ in the name, and not hide in a macro).
If someone can show a regression in swap_duplicate, then probably we should
add a hashlock for the swap_map entries alone (shorts being anatomic), so as
to help the case of the single swap device too.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-04 06:54:41 +08:00
|
|
|
* ->swap_lock (exclusive_swap_page, others)
|
|
|
|
* ->mapping->tree_lock
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2006-01-10 07:59:24 +08:00
|
|
|
* ->i_mutex
|
2005-04-17 06:20:36 +08:00
|
|
|
* ->i_mmap_lock (truncate->unmap_mapping_range)
|
|
|
|
*
|
|
|
|
* ->mmap_sem
|
|
|
|
* ->i_mmap_lock
|
2005-10-30 09:16:41 +08:00
|
|
|
* ->page_table_lock or pte_lock (various, mainly in memory.c)
|
2005-04-17 06:20:36 +08:00
|
|
|
* ->mapping->tree_lock (arch-dependent flush_dcache_mmap_lock)
|
|
|
|
*
|
|
|
|
* ->mmap_sem
|
|
|
|
* ->lock_page (access_process_vm)
|
|
|
|
*
|
|
|
|
* ->mmap_sem
|
2006-01-10 07:59:24 +08:00
|
|
|
* ->i_mutex (msync)
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2006-01-10 07:59:24 +08:00
|
|
|
* ->i_mutex
|
2005-04-17 06:20:36 +08:00
|
|
|
* ->i_alloc_sem (various)
|
|
|
|
*
|
|
|
|
* ->inode_lock
|
|
|
|
* ->sb_lock (fs/fs-writeback.c)
|
|
|
|
* ->mapping->tree_lock (__sync_single_inode)
|
|
|
|
*
|
|
|
|
* ->i_mmap_lock
|
|
|
|
* ->anon_vma.lock (vma_adjust)
|
|
|
|
*
|
|
|
|
* ->anon_vma.lock
|
2005-10-30 09:16:41 +08:00
|
|
|
* ->page_table_lock or pte_lock (anon_vma_prepare and various)
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2005-10-30 09:16:41 +08:00
|
|
|
* ->page_table_lock or pte_lock
|
[PATCH] swap: swap_lock replace list+device
The idea of a swap_device_lock per device, and a swap_list_lock over them all,
is appealing; but in practice almost every holder of swap_device_lock must
already hold swap_list_lock, which defeats the purpose of the split.
The only exceptions have been swap_duplicate, valid_swaphandles and an
untrodden path in try_to_unuse (plus a few places added in this series).
valid_swaphandles doesn't show up high in profiles, but swap_duplicate does
demand attention. However, with the hold time in get_swap_pages so much
reduced, I've not yet found a load and set of swap device priorities to show
even swap_duplicate benefitting from the split. Certainly the split is mere
overhead in the common case of a single swap device.
So, replace swap_list_lock and swap_device_lock by spinlock_t swap_lock
(generally we seem to prefer an _ in the name, and not hide in a macro).
If someone can show a regression in swap_duplicate, then probably we should
add a hashlock for the swap_map entries alone (shorts being anatomic), so as
to help the case of the single swap device too.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-04 06:54:41 +08:00
|
|
|
* ->swap_lock (try_to_unmap_one)
|
2005-04-17 06:20:36 +08:00
|
|
|
* ->private_lock (try_to_unmap_one)
|
|
|
|
* ->tree_lock (try_to_unmap_one)
|
|
|
|
* ->zone.lru_lock (follow_page->mark_page_accessed)
|
2006-01-19 09:42:27 +08:00
|
|
|
* ->zone.lru_lock (check_pte_range->isolate_lru_page)
|
2005-04-17 06:20:36 +08:00
|
|
|
* ->private_lock (page_remove_rmap->set_page_dirty)
|
|
|
|
* ->tree_lock (page_remove_rmap->set_page_dirty)
|
|
|
|
* ->inode_lock (page_remove_rmap->set_page_dirty)
|
|
|
|
* ->inode_lock (zap_pte_range->set_page_dirty)
|
|
|
|
* ->private_lock (zap_pte_range->__set_page_dirty_buffers)
|
|
|
|
*
|
|
|
|
* ->task->proc_lock
|
|
|
|
* ->dcache_lock (proc_pid_lookup)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a page from the page cache and free it. Caller has to make
|
|
|
|
* sure the page is locked and that nobody else uses it - or that usage
|
|
|
|
* is safe. The caller must hold a write_lock on the mapping's tree_lock.
|
|
|
|
*/
|
|
|
|
void __remove_from_page_cache(struct page *page)
|
|
|
|
{
|
|
|
|
struct address_space *mapping = page->mapping;
|
|
|
|
|
|
|
|
radix_tree_delete(&mapping->page_tree, page->index);
|
|
|
|
page->mapping = NULL;
|
|
|
|
mapping->nrpages--;
|
|
|
|
pagecache_acct(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove_from_page_cache(struct page *page)
|
|
|
|
{
|
|
|
|
struct address_space *mapping = page->mapping;
|
|
|
|
|
2005-05-01 23:59:01 +08:00
|
|
|
BUG_ON(!PageLocked(page));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
write_lock_irq(&mapping->tree_lock);
|
|
|
|
__remove_from_page_cache(page);
|
|
|
|
write_unlock_irq(&mapping->tree_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sync_page(void *word)
|
|
|
|
{
|
|
|
|
struct address_space *mapping;
|
|
|
|
struct page *page;
|
|
|
|
|
2005-11-06 00:25:53 +08:00
|
|
|
page = container_of((unsigned long *)word, struct page, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
2005-05-01 23:58:38 +08:00
|
|
|
* page_mapping() is being called without PG_locked held.
|
|
|
|
* Some knowledge of the state and use of the page is used to
|
|
|
|
* reduce the requirements down to a memory barrier.
|
|
|
|
* The danger here is of a stale page_mapping() return value
|
|
|
|
* indicating a struct address_space different from the one it's
|
|
|
|
* associated with when it is associated with one.
|
|
|
|
* After smp_mb(), it's either the correct page_mapping() for
|
|
|
|
* the page, or an old page_mapping() and the page's own
|
|
|
|
* page_mapping() has gone NULL.
|
|
|
|
* The ->sync_page() address_space operation must tolerate
|
|
|
|
* page_mapping() going NULL. By an amazing coincidence,
|
|
|
|
* this comes about because none of the users of the page
|
|
|
|
* in the ->sync_page() methods make essential use of the
|
|
|
|
* page_mapping(), merely passing the page down to the backing
|
|
|
|
* device's unplug functions when it's non-NULL, which in turn
|
[PATCH] mm: split page table lock
Christoph Lameter demonstrated very poor scalability on the SGI 512-way, with
a many-threaded application which concurrently initializes different parts of
a large anonymous area.
This patch corrects that, by using a separate spinlock per page table page, to
guard the page table entries in that page, instead of using the mm's single
page_table_lock. (But even then, page_table_lock is still used to guard page
table allocation, and anon_vma allocation.)
In this implementation, the spinlock is tucked inside the struct page of the
page table page: with a BUILD_BUG_ON in case it overflows - which it would in
the case of 32-bit PA-RISC with spinlock debugging enabled.
Splitting the lock is not quite for free: another cacheline access. Ideally,
I suppose we would use split ptlock only for multi-threaded processes on
multi-cpu machines; but deciding that dynamically would have its own costs.
So for now enable it by config, at some number of cpus - since the Kconfig
language doesn't support inequalities, let preprocessor compare that with
NR_CPUS. But I don't think it's worth being user-configurable: for good
testing of both split and unsplit configs, split now at 4 cpus, and perhaps
change that to 8 later.
There is a benefit even for singly threaded processes: kswapd can be attacking
one part of the mm while another part is busy faulting.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-30 09:16:40 +08:00
|
|
|
* ignore it for all cases but swap, where only page_private(page) is
|
2005-05-01 23:58:38 +08:00
|
|
|
* of interest. When page_mapping() does go NULL, the entire
|
|
|
|
* call stack gracefully ignores the page and returns.
|
|
|
|
* -- wli
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
smp_mb();
|
|
|
|
mapping = page_mapping(page);
|
|
|
|
if (mapping && mapping->a_ops && mapping->a_ops->sync_page)
|
|
|
|
mapping->a_ops->sync_page(page);
|
|
|
|
io_schedule();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-06-23 17:03:49 +08:00
|
|
|
* __filemap_fdatawrite_range - start writeback on mapping dirty pages in range
|
2005-05-01 23:59:26 +08:00
|
|
|
* @mapping: address space structure to write
|
|
|
|
* @start: offset in bytes where the range starts
|
2006-03-24 19:17:45 +08:00
|
|
|
* @end: offset in bytes where the range ends (inclusive)
|
2005-05-01 23:59:26 +08:00
|
|
|
* @sync_mode: enable synchronous operation
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2006-06-23 17:03:49 +08:00
|
|
|
* Start writeback against all of a mapping's dirty pages that lie
|
|
|
|
* within the byte offsets <start, end> inclusive.
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* If sync_mode is WB_SYNC_ALL then this is a "data integrity" operation, as
|
2006-06-23 17:03:49 +08:00
|
|
|
* opposed to a regular memory cleansing writeback. The difference between
|
2005-04-17 06:20:36 +08:00
|
|
|
* these two operations is that if a dirty page/buffer is encountered, it must
|
|
|
|
* be waited upon, and not just skipped over.
|
|
|
|
*/
|
[PATCH] fadvise(): write commands
Add two new linux-specific fadvise extensions():
LINUX_FADV_ASYNC_WRITE: start async writeout of any dirty pages between file
offsets `offset' and `offset+len'. Any pages which are currently under
writeout are skipped, whether or not they are dirty.
LINUX_FADV_WRITE_WAIT: wait upon writeout of any dirty pages between file
offsets `offset' and `offset+len'.
By combining these two operations the application may do several things:
LINUX_FADV_ASYNC_WRITE: push some or all of the dirty pages at the disk.
LINUX_FADV_WRITE_WAIT, LINUX_FADV_ASYNC_WRITE: push all of the currently dirty
pages at the disk.
LINUX_FADV_WRITE_WAIT, LINUX_FADV_ASYNC_WRITE, LINUX_FADV_WRITE_WAIT: push all
of the currently dirty pages at the disk, wait until they have been written.
It should be noted that none of these operations write out the file's
metadata. So unless the application is strictly performing overwrites of
already-instantiated disk blocks, there are no guarantees here that the data
will be available after a crash.
To complete this suite of operations I guess we should have a "sync file
metadata only" operation. This gives applications access to all the building
blocks needed for all sorts of sync operations. But sync-metadata doesn't fit
well with the fadvise() interface. Probably it should be a new syscall:
sys_fmetadatasync().
The patch also diddles with the meaning of `endbyte' in sys_fadvise64_64().
It is made to represent that last affected byte in the file (ie: it is
inclusive). Generally, all these byterange and pagerange functions are
inclusive so we can easily represent EOF with -1.
As Ulrich notes, these two functions are somewhat abusive of the fadvise()
concept, which appears to be "set the future policy for this fd".
But these commands are a perfect fit with the fadvise() impementation, and
several of the existing fadvise() commands are synchronous and don't affect
future policy either. I think we can live with the slight incongruity.
Cc: Michael Kerrisk <mtk-manpages@gmx.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 19:18:04 +08:00
|
|
|
int __filemap_fdatawrite_range(struct address_space *mapping, loff_t start,
|
|
|
|
loff_t end, int sync_mode)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct writeback_control wbc = {
|
|
|
|
.sync_mode = sync_mode,
|
|
|
|
.nr_to_write = mapping->nrpages * 2,
|
[PATCH] writeback: fix range handling
When a writeback_control's `start' and `end' fields are used to
indicate a one-byte-range starting at file offset zero, the required
values of .start=0,.end=0 mean that the ->writepages() implementation
has no way of telling that it is being asked to perform a range
request. Because we're currently overloading (start == 0 && end == 0)
to mean "this is not a write-a-range request".
To make all this sane, the patch changes range of writeback_control.
So caller does: If it is calling ->writepages() to write pages, it
sets range (range_start/end or range_cyclic) always.
And if range_cyclic is true, ->writepages() thinks the range is
cyclic, otherwise it just uses range_start and range_end.
This patch does,
- Add LLONG_MAX, LLONG_MIN, ULLONG_MAX to include/linux/kernel.h
-1 is usually ok for range_end (type is long long). But, if someone did,
range_end += val; range_end is "val - 1"
u64val = range_end >> bits; u64val is "~(0ULL)"
or something, they are wrong. So, this adds LLONG_MAX to avoid nasty
things, and uses LLONG_MAX for range_end.
- All callers of ->writepages() sets range_start/end or range_cyclic.
- Fix updates of ->writeback_index. It seems already bit strange.
If it starts at 0 and ended by check of nr_to_write, this last
index may reduce chance to scan end of file. So, this updates
->writeback_index only if range_cyclic is true or whole-file is
scanned.
Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Nathan Scott <nathans@sgi.com>
Cc: Anton Altaparmakov <aia21@cantab.net>
Cc: Steven French <sfrench@us.ibm.com>
Cc: "Vladimir V. Saveliev" <vs@namesys.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-23 17:03:26 +08:00
|
|
|
.range_start = start,
|
|
|
|
.range_end = end,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (!mapping_cap_writeback_dirty(mapping))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ret = do_writepages(mapping, &wbc);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int __filemap_fdatawrite(struct address_space *mapping,
|
|
|
|
int sync_mode)
|
|
|
|
{
|
[PATCH] writeback: fix range handling
When a writeback_control's `start' and `end' fields are used to
indicate a one-byte-range starting at file offset zero, the required
values of .start=0,.end=0 mean that the ->writepages() implementation
has no way of telling that it is being asked to perform a range
request. Because we're currently overloading (start == 0 && end == 0)
to mean "this is not a write-a-range request".
To make all this sane, the patch changes range of writeback_control.
So caller does: If it is calling ->writepages() to write pages, it
sets range (range_start/end or range_cyclic) always.
And if range_cyclic is true, ->writepages() thinks the range is
cyclic, otherwise it just uses range_start and range_end.
This patch does,
- Add LLONG_MAX, LLONG_MIN, ULLONG_MAX to include/linux/kernel.h
-1 is usually ok for range_end (type is long long). But, if someone did,
range_end += val; range_end is "val - 1"
u64val = range_end >> bits; u64val is "~(0ULL)"
or something, they are wrong. So, this adds LLONG_MAX to avoid nasty
things, and uses LLONG_MAX for range_end.
- All callers of ->writepages() sets range_start/end or range_cyclic.
- Fix updates of ->writeback_index. It seems already bit strange.
If it starts at 0 and ended by check of nr_to_write, this last
index may reduce chance to scan end of file. So, this updates
->writeback_index only if range_cyclic is true or whole-file is
scanned.
Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Nathan Scott <nathans@sgi.com>
Cc: Anton Altaparmakov <aia21@cantab.net>
Cc: Steven French <sfrench@us.ibm.com>
Cc: "Vladimir V. Saveliev" <vs@namesys.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-23 17:03:26 +08:00
|
|
|
return __filemap_fdatawrite_range(mapping, 0, LLONG_MAX, sync_mode);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int filemap_fdatawrite(struct address_space *mapping)
|
|
|
|
{
|
|
|
|
return __filemap_fdatawrite(mapping, WB_SYNC_ALL);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(filemap_fdatawrite);
|
|
|
|
|
[PATCH] fadvise(): write commands
Add two new linux-specific fadvise extensions():
LINUX_FADV_ASYNC_WRITE: start async writeout of any dirty pages between file
offsets `offset' and `offset+len'. Any pages which are currently under
writeout are skipped, whether or not they are dirty.
LINUX_FADV_WRITE_WAIT: wait upon writeout of any dirty pages between file
offsets `offset' and `offset+len'.
By combining these two operations the application may do several things:
LINUX_FADV_ASYNC_WRITE: push some or all of the dirty pages at the disk.
LINUX_FADV_WRITE_WAIT, LINUX_FADV_ASYNC_WRITE: push all of the currently dirty
pages at the disk.
LINUX_FADV_WRITE_WAIT, LINUX_FADV_ASYNC_WRITE, LINUX_FADV_WRITE_WAIT: push all
of the currently dirty pages at the disk, wait until they have been written.
It should be noted that none of these operations write out the file's
metadata. So unless the application is strictly performing overwrites of
already-instantiated disk blocks, there are no guarantees here that the data
will be available after a crash.
To complete this suite of operations I guess we should have a "sync file
metadata only" operation. This gives applications access to all the building
blocks needed for all sorts of sync operations. But sync-metadata doesn't fit
well with the fadvise() interface. Probably it should be a new syscall:
sys_fmetadatasync().
The patch also diddles with the meaning of `endbyte' in sys_fadvise64_64().
It is made to represent that last affected byte in the file (ie: it is
inclusive). Generally, all these byterange and pagerange functions are
inclusive so we can easily represent EOF with -1.
As Ulrich notes, these two functions are somewhat abusive of the fadvise()
concept, which appears to be "set the future policy for this fd".
But these commands are a perfect fit with the fadvise() impementation, and
several of the existing fadvise() commands are synchronous and don't affect
future policy either. I think we can live with the slight incongruity.
Cc: Michael Kerrisk <mtk-manpages@gmx.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 19:18:04 +08:00
|
|
|
static int filemap_fdatawrite_range(struct address_space *mapping, loff_t start,
|
|
|
|
loff_t end)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return __filemap_fdatawrite_range(mapping, start, end, WB_SYNC_ALL);
|
|
|
|
}
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* filemap_flush - mostly a non-blocking flush
|
|
|
|
* @mapping: target address_space
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* This is a mostly non-blocking flush. Not suitable for data-integrity
|
|
|
|
* purposes - I/O may not be started against all dirty pages.
|
|
|
|
*/
|
|
|
|
int filemap_flush(struct address_space *mapping)
|
|
|
|
{
|
|
|
|
return __filemap_fdatawrite(mapping, WB_SYNC_NONE);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(filemap_flush);
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* wait_on_page_writeback_range - wait for writeback to complete
|
|
|
|
* @mapping: target address_space
|
|
|
|
* @start: beginning page index
|
|
|
|
* @end: ending page index
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Wait for writeback to complete against pages indexed by start->end
|
|
|
|
* inclusive
|
|
|
|
*/
|
[PATCH] fadvise(): write commands
Add two new linux-specific fadvise extensions():
LINUX_FADV_ASYNC_WRITE: start async writeout of any dirty pages between file
offsets `offset' and `offset+len'. Any pages which are currently under
writeout are skipped, whether or not they are dirty.
LINUX_FADV_WRITE_WAIT: wait upon writeout of any dirty pages between file
offsets `offset' and `offset+len'.
By combining these two operations the application may do several things:
LINUX_FADV_ASYNC_WRITE: push some or all of the dirty pages at the disk.
LINUX_FADV_WRITE_WAIT, LINUX_FADV_ASYNC_WRITE: push all of the currently dirty
pages at the disk.
LINUX_FADV_WRITE_WAIT, LINUX_FADV_ASYNC_WRITE, LINUX_FADV_WRITE_WAIT: push all
of the currently dirty pages at the disk, wait until they have been written.
It should be noted that none of these operations write out the file's
metadata. So unless the application is strictly performing overwrites of
already-instantiated disk blocks, there are no guarantees here that the data
will be available after a crash.
To complete this suite of operations I guess we should have a "sync file
metadata only" operation. This gives applications access to all the building
blocks needed for all sorts of sync operations. But sync-metadata doesn't fit
well with the fadvise() interface. Probably it should be a new syscall:
sys_fmetadatasync().
The patch also diddles with the meaning of `endbyte' in sys_fadvise64_64().
It is made to represent that last affected byte in the file (ie: it is
inclusive). Generally, all these byterange and pagerange functions are
inclusive so we can easily represent EOF with -1.
As Ulrich notes, these two functions are somewhat abusive of the fadvise()
concept, which appears to be "set the future policy for this fd".
But these commands are a perfect fit with the fadvise() impementation, and
several of the existing fadvise() commands are synchronous and don't affect
future policy either. I think we can live with the slight incongruity.
Cc: Michael Kerrisk <mtk-manpages@gmx.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-24 19:18:04 +08:00
|
|
|
int wait_on_page_writeback_range(struct address_space *mapping,
|
2005-04-17 06:20:36 +08:00
|
|
|
pgoff_t start, pgoff_t end)
|
|
|
|
{
|
|
|
|
struct pagevec pvec;
|
|
|
|
int nr_pages;
|
|
|
|
int ret = 0;
|
|
|
|
pgoff_t index;
|
|
|
|
|
|
|
|
if (end < start)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pagevec_init(&pvec, 0);
|
|
|
|
index = start;
|
|
|
|
while ((index <= end) &&
|
|
|
|
(nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
|
|
|
|
PAGECACHE_TAG_WRITEBACK,
|
|
|
|
min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1)) != 0) {
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for (i = 0; i < nr_pages; i++) {
|
|
|
|
struct page *page = pvec.pages[i];
|
|
|
|
|
|
|
|
/* until radix tree lookup accepts end_index */
|
|
|
|
if (page->index > end)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
wait_on_page_writeback(page);
|
|
|
|
if (PageError(page))
|
|
|
|
ret = -EIO;
|
|
|
|
}
|
|
|
|
pagevec_release(&pvec);
|
|
|
|
cond_resched();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for outstanding write errors */
|
|
|
|
if (test_and_clear_bit(AS_ENOSPC, &mapping->flags))
|
|
|
|
ret = -ENOSPC;
|
|
|
|
if (test_and_clear_bit(AS_EIO, &mapping->flags))
|
|
|
|
ret = -EIO;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* sync_page_range - write and wait on all pages in the passed range
|
|
|
|
* @inode: target inode
|
|
|
|
* @mapping: target address_space
|
|
|
|
* @pos: beginning offset in pages to write
|
|
|
|
* @count: number of bytes to write
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Write and wait upon all the pages in the passed range. This is a "data
|
|
|
|
* integrity" operation. It waits upon in-flight writeout before starting and
|
|
|
|
* waiting upon new writeout. If there was an IO error, return it.
|
|
|
|
*
|
2006-01-10 07:59:24 +08:00
|
|
|
* We need to re-take i_mutex during the generic_osync_inode list walk because
|
2005-04-17 06:20:36 +08:00
|
|
|
* it is otherwise livelockable.
|
|
|
|
*/
|
|
|
|
int sync_page_range(struct inode *inode, struct address_space *mapping,
|
2006-01-08 17:02:12 +08:00
|
|
|
loff_t pos, loff_t count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
pgoff_t start = pos >> PAGE_CACHE_SHIFT;
|
|
|
|
pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!mapping_cap_writeback_dirty(mapping) || !count)
|
|
|
|
return 0;
|
|
|
|
ret = filemap_fdatawrite_range(mapping, pos, pos + count - 1);
|
|
|
|
if (ret == 0) {
|
2006-01-10 07:59:24 +08:00
|
|
|
mutex_lock(&inode->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = generic_osync_inode(inode, mapping, OSYNC_METADATA);
|
2006-01-10 07:59:24 +08:00
|
|
|
mutex_unlock(&inode->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
if (ret == 0)
|
|
|
|
ret = wait_on_page_writeback_range(mapping, start, end);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(sync_page_range);
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* sync_page_range_nolock
|
|
|
|
* @inode: target inode
|
|
|
|
* @mapping: target address_space
|
|
|
|
* @pos: beginning offset in pages to write
|
|
|
|
* @count: number of bytes to write
|
|
|
|
*
|
2006-01-10 07:59:24 +08:00
|
|
|
* Note: Holding i_mutex across sync_page_range_nolock is not a good idea
|
2005-04-17 06:20:36 +08:00
|
|
|
* as it forces O_SYNC writers to different parts of the same file
|
|
|
|
* to be serialised right until io completion.
|
|
|
|
*/
|
2006-01-08 17:02:12 +08:00
|
|
|
int sync_page_range_nolock(struct inode *inode, struct address_space *mapping,
|
|
|
|
loff_t pos, loff_t count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
pgoff_t start = pos >> PAGE_CACHE_SHIFT;
|
|
|
|
pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!mapping_cap_writeback_dirty(mapping) || !count)
|
|
|
|
return 0;
|
|
|
|
ret = filemap_fdatawrite_range(mapping, pos, pos + count - 1);
|
|
|
|
if (ret == 0)
|
|
|
|
ret = generic_osync_inode(inode, mapping, OSYNC_METADATA);
|
|
|
|
if (ret == 0)
|
|
|
|
ret = wait_on_page_writeback_range(mapping, start, end);
|
|
|
|
return ret;
|
|
|
|
}
|
2006-01-08 17:02:12 +08:00
|
|
|
EXPORT_SYMBOL(sync_page_range_nolock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/**
|
2006-06-23 17:03:49 +08:00
|
|
|
* filemap_fdatawait - wait for all under-writeback pages to complete
|
2005-04-17 06:20:36 +08:00
|
|
|
* @mapping: address space structure to wait for
|
2006-06-23 17:03:49 +08:00
|
|
|
*
|
|
|
|
* Walk the list of under-writeback pages of the given address space
|
|
|
|
* and wait for all of them.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
int filemap_fdatawait(struct address_space *mapping)
|
|
|
|
{
|
|
|
|
loff_t i_size = i_size_read(mapping->host);
|
|
|
|
|
|
|
|
if (i_size == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return wait_on_page_writeback_range(mapping, 0,
|
|
|
|
(i_size - 1) >> PAGE_CACHE_SHIFT);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(filemap_fdatawait);
|
|
|
|
|
|
|
|
int filemap_write_and_wait(struct address_space *mapping)
|
|
|
|
{
|
[PATCH] Fix and add EXPORT_SYMBOL(filemap_write_and_wait)
This patch add EXPORT_SYMBOL(filemap_write_and_wait) and use it.
See mm/filemap.c:
And changes the filemap_write_and_wait() and filemap_write_and_wait_range().
Current filemap_write_and_wait() doesn't wait if filemap_fdatawrite()
returns error. However, even if filemap_fdatawrite() returned an
error, it may have submitted the partially data pages to the device.
(e.g. in the case of -ENOSPC)
<quotation>
Andrew Morton writes,
If filemap_fdatawrite() returns an error, this might be due to some
I/O problem: dead disk, unplugged cable, etc. Given the generally
crappy quality of the kernel's handling of such exceptions, there's a
good chance that the filemap_fdatawait() will get stuck in D state
forever.
</quotation>
So, this patch doesn't wait if filemap_fdatawrite() returns the -EIO.
Trond, could you please review the nfs part? Especially I'm not sure,
nfs must use the "filemap_fdatawrite(inode->i_mapping) == 0", or not.
Acked-by: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 17:02:14 +08:00
|
|
|
int err = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (mapping->nrpages) {
|
[PATCH] Fix and add EXPORT_SYMBOL(filemap_write_and_wait)
This patch add EXPORT_SYMBOL(filemap_write_and_wait) and use it.
See mm/filemap.c:
And changes the filemap_write_and_wait() and filemap_write_and_wait_range().
Current filemap_write_and_wait() doesn't wait if filemap_fdatawrite()
returns error. However, even if filemap_fdatawrite() returned an
error, it may have submitted the partially data pages to the device.
(e.g. in the case of -ENOSPC)
<quotation>
Andrew Morton writes,
If filemap_fdatawrite() returns an error, this might be due to some
I/O problem: dead disk, unplugged cable, etc. Given the generally
crappy quality of the kernel's handling of such exceptions, there's a
good chance that the filemap_fdatawait() will get stuck in D state
forever.
</quotation>
So, this patch doesn't wait if filemap_fdatawrite() returns the -EIO.
Trond, could you please review the nfs part? Especially I'm not sure,
nfs must use the "filemap_fdatawrite(inode->i_mapping) == 0", or not.
Acked-by: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 17:02:14 +08:00
|
|
|
err = filemap_fdatawrite(mapping);
|
|
|
|
/*
|
|
|
|
* Even if the above returned error, the pages may be
|
|
|
|
* written partially (e.g. -ENOSPC), so we wait for it.
|
|
|
|
* But the -EIO is special case, it may indicate the worst
|
|
|
|
* thing (e.g. bug) happened, so we avoid waiting for it.
|
|
|
|
*/
|
|
|
|
if (err != -EIO) {
|
|
|
|
int err2 = filemap_fdatawait(mapping);
|
|
|
|
if (!err)
|
|
|
|
err = err2;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
[PATCH] Fix and add EXPORT_SYMBOL(filemap_write_and_wait)
This patch add EXPORT_SYMBOL(filemap_write_and_wait) and use it.
See mm/filemap.c:
And changes the filemap_write_and_wait() and filemap_write_and_wait_range().
Current filemap_write_and_wait() doesn't wait if filemap_fdatawrite()
returns error. However, even if filemap_fdatawrite() returned an
error, it may have submitted the partially data pages to the device.
(e.g. in the case of -ENOSPC)
<quotation>
Andrew Morton writes,
If filemap_fdatawrite() returns an error, this might be due to some
I/O problem: dead disk, unplugged cable, etc. Given the generally
crappy quality of the kernel's handling of such exceptions, there's a
good chance that the filemap_fdatawait() will get stuck in D state
forever.
</quotation>
So, this patch doesn't wait if filemap_fdatawrite() returns the -EIO.
Trond, could you please review the nfs part? Especially I'm not sure,
nfs must use the "filemap_fdatawrite(inode->i_mapping) == 0", or not.
Acked-by: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 17:02:14 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
[PATCH] Fix and add EXPORT_SYMBOL(filemap_write_and_wait)
This patch add EXPORT_SYMBOL(filemap_write_and_wait) and use it.
See mm/filemap.c:
And changes the filemap_write_and_wait() and filemap_write_and_wait_range().
Current filemap_write_and_wait() doesn't wait if filemap_fdatawrite()
returns error. However, even if filemap_fdatawrite() returned an
error, it may have submitted the partially data pages to the device.
(e.g. in the case of -ENOSPC)
<quotation>
Andrew Morton writes,
If filemap_fdatawrite() returns an error, this might be due to some
I/O problem: dead disk, unplugged cable, etc. Given the generally
crappy quality of the kernel's handling of such exceptions, there's a
good chance that the filemap_fdatawait() will get stuck in D state
forever.
</quotation>
So, this patch doesn't wait if filemap_fdatawrite() returns the -EIO.
Trond, could you please review the nfs part? Especially I'm not sure,
nfs must use the "filemap_fdatawrite(inode->i_mapping) == 0", or not.
Acked-by: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 17:02:14 +08:00
|
|
|
EXPORT_SYMBOL(filemap_write_and_wait);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* filemap_write_and_wait_range - write out & wait on a file range
|
|
|
|
* @mapping: the address_space for the pages
|
|
|
|
* @lstart: offset in bytes where the range starts
|
|
|
|
* @lend: offset in bytes where the range ends (inclusive)
|
|
|
|
*
|
2006-03-24 19:17:45 +08:00
|
|
|
* Write out and wait upon file offsets lstart->lend, inclusive.
|
|
|
|
*
|
|
|
|
* Note that `lend' is inclusive (describes the last byte to be written) so
|
|
|
|
* that this function can be used to write to the very end-of-file (end = -1).
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
int filemap_write_and_wait_range(struct address_space *mapping,
|
|
|
|
loff_t lstart, loff_t lend)
|
|
|
|
{
|
[PATCH] Fix and add EXPORT_SYMBOL(filemap_write_and_wait)
This patch add EXPORT_SYMBOL(filemap_write_and_wait) and use it.
See mm/filemap.c:
And changes the filemap_write_and_wait() and filemap_write_and_wait_range().
Current filemap_write_and_wait() doesn't wait if filemap_fdatawrite()
returns error. However, even if filemap_fdatawrite() returned an
error, it may have submitted the partially data pages to the device.
(e.g. in the case of -ENOSPC)
<quotation>
Andrew Morton writes,
If filemap_fdatawrite() returns an error, this might be due to some
I/O problem: dead disk, unplugged cable, etc. Given the generally
crappy quality of the kernel's handling of such exceptions, there's a
good chance that the filemap_fdatawait() will get stuck in D state
forever.
</quotation>
So, this patch doesn't wait if filemap_fdatawrite() returns the -EIO.
Trond, could you please review the nfs part? Especially I'm not sure,
nfs must use the "filemap_fdatawrite(inode->i_mapping) == 0", or not.
Acked-by: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 17:02:14 +08:00
|
|
|
int err = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (mapping->nrpages) {
|
[PATCH] Fix and add EXPORT_SYMBOL(filemap_write_and_wait)
This patch add EXPORT_SYMBOL(filemap_write_and_wait) and use it.
See mm/filemap.c:
And changes the filemap_write_and_wait() and filemap_write_and_wait_range().
Current filemap_write_and_wait() doesn't wait if filemap_fdatawrite()
returns error. However, even if filemap_fdatawrite() returned an
error, it may have submitted the partially data pages to the device.
(e.g. in the case of -ENOSPC)
<quotation>
Andrew Morton writes,
If filemap_fdatawrite() returns an error, this might be due to some
I/O problem: dead disk, unplugged cable, etc. Given the generally
crappy quality of the kernel's handling of such exceptions, there's a
good chance that the filemap_fdatawait() will get stuck in D state
forever.
</quotation>
So, this patch doesn't wait if filemap_fdatawrite() returns the -EIO.
Trond, could you please review the nfs part? Especially I'm not sure,
nfs must use the "filemap_fdatawrite(inode->i_mapping) == 0", or not.
Acked-by: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 17:02:14 +08:00
|
|
|
err = __filemap_fdatawrite_range(mapping, lstart, lend,
|
|
|
|
WB_SYNC_ALL);
|
|
|
|
/* See comment of filemap_write_and_wait() */
|
|
|
|
if (err != -EIO) {
|
|
|
|
int err2 = wait_on_page_writeback_range(mapping,
|
|
|
|
lstart >> PAGE_CACHE_SHIFT,
|
|
|
|
lend >> PAGE_CACHE_SHIFT);
|
|
|
|
if (!err)
|
|
|
|
err = err2;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
[PATCH] Fix and add EXPORT_SYMBOL(filemap_write_and_wait)
This patch add EXPORT_SYMBOL(filemap_write_and_wait) and use it.
See mm/filemap.c:
And changes the filemap_write_and_wait() and filemap_write_and_wait_range().
Current filemap_write_and_wait() doesn't wait if filemap_fdatawrite()
returns error. However, even if filemap_fdatawrite() returned an
error, it may have submitted the partially data pages to the device.
(e.g. in the case of -ENOSPC)
<quotation>
Andrew Morton writes,
If filemap_fdatawrite() returns an error, this might be due to some
I/O problem: dead disk, unplugged cable, etc. Given the generally
crappy quality of the kernel's handling of such exceptions, there's a
good chance that the filemap_fdatawait() will get stuck in D state
forever.
</quotation>
So, this patch doesn't wait if filemap_fdatawrite() returns the -EIO.
Trond, could you please review the nfs part? Especially I'm not sure,
nfs must use the "filemap_fdatawrite(inode->i_mapping) == 0", or not.
Acked-by: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 17:02:14 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* add_to_page_cache - add newly allocated pagecache pages
|
|
|
|
* @page: page to add
|
|
|
|
* @mapping: the page's address_space
|
|
|
|
* @offset: page index
|
|
|
|
* @gfp_mask: page allocation mode
|
|
|
|
*
|
|
|
|
* This function is used to add newly allocated pagecache pages;
|
2005-04-17 06:20:36 +08:00
|
|
|
* the page is new, so we can just run SetPageLocked() against it.
|
|
|
|
* The other page state flags were set by rmqueue().
|
|
|
|
*
|
|
|
|
* This function does not add the page to the LRU. The caller must do that.
|
|
|
|
*/
|
|
|
|
int add_to_page_cache(struct page *page, struct address_space *mapping,
|
2005-10-21 15:18:50 +08:00
|
|
|
pgoff_t offset, gfp_t gfp_mask)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);
|
|
|
|
|
|
|
|
if (error == 0) {
|
|
|
|
write_lock_irq(&mapping->tree_lock);
|
|
|
|
error = radix_tree_insert(&mapping->page_tree, offset, page);
|
|
|
|
if (!error) {
|
|
|
|
page_cache_get(page);
|
|
|
|
SetPageLocked(page);
|
|
|
|
page->mapping = mapping;
|
|
|
|
page->index = offset;
|
|
|
|
mapping->nrpages++;
|
|
|
|
pagecache_acct(1);
|
|
|
|
}
|
|
|
|
write_unlock_irq(&mapping->tree_lock);
|
|
|
|
radix_tree_preload_end();
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(add_to_page_cache);
|
|
|
|
|
|
|
|
int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
|
2005-10-21 15:18:50 +08:00
|
|
|
pgoff_t offset, gfp_t gfp_mask)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int ret = add_to_page_cache(page, mapping, offset, gfp_mask);
|
|
|
|
if (ret == 0)
|
|
|
|
lru_cache_add(page);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-03-24 19:16:04 +08:00
|
|
|
#ifdef CONFIG_NUMA
|
|
|
|
struct page *page_cache_alloc(struct address_space *x)
|
|
|
|
{
|
|
|
|
if (cpuset_do_page_mem_spread()) {
|
|
|
|
int n = cpuset_mem_spread_node();
|
|
|
|
return alloc_pages_node(n, mapping_gfp_mask(x), 0);
|
|
|
|
}
|
|
|
|
return alloc_pages(mapping_gfp_mask(x), 0);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(page_cache_alloc);
|
|
|
|
|
|
|
|
struct page *page_cache_alloc_cold(struct address_space *x)
|
|
|
|
{
|
|
|
|
if (cpuset_do_page_mem_spread()) {
|
|
|
|
int n = cpuset_mem_spread_node();
|
|
|
|
return alloc_pages_node(n, mapping_gfp_mask(x)|__GFP_COLD, 0);
|
|
|
|
}
|
|
|
|
return alloc_pages(mapping_gfp_mask(x)|__GFP_COLD, 0);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(page_cache_alloc_cold);
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* In order to wait for pages to become available there must be
|
|
|
|
* waitqueues associated with pages. By using a hash table of
|
|
|
|
* waitqueues where the bucket discipline is to maintain all
|
|
|
|
* waiters on the same queue and wake all when any of the pages
|
|
|
|
* become available, and for the woken contexts to check to be
|
|
|
|
* sure the appropriate page became available, this saves space
|
|
|
|
* at a cost of "thundering herd" phenomena during rare hash
|
|
|
|
* collisions.
|
|
|
|
*/
|
|
|
|
static wait_queue_head_t *page_waitqueue(struct page *page)
|
|
|
|
{
|
|
|
|
const struct zone *zone = page_zone(page);
|
|
|
|
|
|
|
|
return &zone->wait_table[hash_ptr(page, zone->wait_table_bits)];
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void wake_up_page(struct page *page, int bit)
|
|
|
|
{
|
|
|
|
__wake_up_bit(page_waitqueue(page), &page->flags, bit);
|
|
|
|
}
|
|
|
|
|
|
|
|
void fastcall wait_on_page_bit(struct page *page, int bit_nr)
|
|
|
|
{
|
|
|
|
DEFINE_WAIT_BIT(wait, &page->flags, bit_nr);
|
|
|
|
|
|
|
|
if (test_bit(bit_nr, &page->flags))
|
|
|
|
__wait_on_bit(page_waitqueue(page), &wait, sync_page,
|
|
|
|
TASK_UNINTERRUPTIBLE);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(wait_on_page_bit);
|
|
|
|
|
|
|
|
/**
|
2006-06-23 17:03:49 +08:00
|
|
|
* unlock_page - unlock a locked page
|
2005-04-17 06:20:36 +08:00
|
|
|
* @page: the page
|
|
|
|
*
|
|
|
|
* Unlocks the page and wakes up sleepers in ___wait_on_page_locked().
|
|
|
|
* Also wakes sleepers in wait_on_page_writeback() because the wakeup
|
|
|
|
* mechananism between PageLocked pages and PageWriteback pages is shared.
|
|
|
|
* But that's OK - sleepers in wait_on_page_writeback() just go back to sleep.
|
|
|
|
*
|
|
|
|
* The first mb is necessary to safely close the critical section opened by the
|
|
|
|
* TestSetPageLocked(), the second mb is necessary to enforce ordering between
|
|
|
|
* the clear_bit and the read of the waitqueue (to avoid SMP races with a
|
|
|
|
* parallel wait_on_page_locked()).
|
|
|
|
*/
|
|
|
|
void fastcall unlock_page(struct page *page)
|
|
|
|
{
|
|
|
|
smp_mb__before_clear_bit();
|
|
|
|
if (!TestClearPageLocked(page))
|
|
|
|
BUG();
|
|
|
|
smp_mb__after_clear_bit();
|
|
|
|
wake_up_page(page, PG_locked);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(unlock_page);
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* end_page_writeback - end writeback against a page
|
|
|
|
* @page: the page
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void end_page_writeback(struct page *page)
|
|
|
|
{
|
|
|
|
if (!TestClearPageReclaim(page) || rotate_reclaimable_page(page)) {
|
|
|
|
if (!test_clear_page_writeback(page))
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
smp_mb__after_clear_bit();
|
|
|
|
wake_up_page(page, PG_writeback);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(end_page_writeback);
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* __lock_page - get a lock on the page, assuming we need to sleep to get it
|
|
|
|
* @page: the page to lock
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2006-06-23 17:03:49 +08:00
|
|
|
* Ugly. Running sync_page() in state TASK_UNINTERRUPTIBLE is scary. If some
|
2005-04-17 06:20:36 +08:00
|
|
|
* random driver's requestfn sets TASK_RUNNING, we could busywait. However
|
|
|
|
* chances are that on the second loop, the block layer's plug list is empty,
|
|
|
|
* so sync_page() will then return in state TASK_UNINTERRUPTIBLE.
|
|
|
|
*/
|
|
|
|
void fastcall __lock_page(struct page *page)
|
|
|
|
{
|
|
|
|
DEFINE_WAIT_BIT(wait, &page->flags, PG_locked);
|
|
|
|
|
|
|
|
__wait_on_bit_lock(page_waitqueue(page), &wait, sync_page,
|
|
|
|
TASK_UNINTERRUPTIBLE);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__lock_page);
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* find_get_page - find and get a page reference
|
|
|
|
* @mapping: the address_space to search
|
|
|
|
* @offset: the page index
|
|
|
|
*
|
|
|
|
* A rather lightweight function, finding and getting a reference to a
|
2005-04-17 06:20:36 +08:00
|
|
|
* hashed page atomically.
|
|
|
|
*/
|
|
|
|
struct page * find_get_page(struct address_space *mapping, unsigned long offset)
|
|
|
|
{
|
|
|
|
struct page *page;
|
|
|
|
|
|
|
|
read_lock_irq(&mapping->tree_lock);
|
|
|
|
page = radix_tree_lookup(&mapping->page_tree, offset);
|
|
|
|
if (page)
|
|
|
|
page_cache_get(page);
|
|
|
|
read_unlock_irq(&mapping->tree_lock);
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(find_get_page);
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* find_trylock_page - find and lock a page
|
|
|
|
* @mapping: the address_space to search
|
|
|
|
* @offset: the page index
|
|
|
|
*
|
|
|
|
* Same as find_get_page(), but trylock it instead of incrementing the count.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
struct page *find_trylock_page(struct address_space *mapping, unsigned long offset)
|
|
|
|
{
|
|
|
|
struct page *page;
|
|
|
|
|
|
|
|
read_lock_irq(&mapping->tree_lock);
|
|
|
|
page = radix_tree_lookup(&mapping->page_tree, offset);
|
|
|
|
if (page && TestSetPageLocked(page))
|
|
|
|
page = NULL;
|
|
|
|
read_unlock_irq(&mapping->tree_lock);
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(find_trylock_page);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* find_lock_page - locate, pin and lock a pagecache page
|
2005-05-01 23:59:26 +08:00
|
|
|
* @mapping: the address_space to search
|
|
|
|
* @offset: the page index
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Locates the desired pagecache page, locks it, increments its reference
|
|
|
|
* count and returns its address.
|
|
|
|
*
|
|
|
|
* Returns zero if the page was not present. find_lock_page() may sleep.
|
|
|
|
*/
|
|
|
|
struct page *find_lock_page(struct address_space *mapping,
|
|
|
|
unsigned long offset)
|
|
|
|
{
|
|
|
|
struct page *page;
|
|
|
|
|
|
|
|
read_lock_irq(&mapping->tree_lock);
|
|
|
|
repeat:
|
|
|
|
page = radix_tree_lookup(&mapping->page_tree, offset);
|
|
|
|
if (page) {
|
|
|
|
page_cache_get(page);
|
|
|
|
if (TestSetPageLocked(page)) {
|
|
|
|
read_unlock_irq(&mapping->tree_lock);
|
2006-01-06 16:11:08 +08:00
|
|
|
__lock_page(page);
|
2005-04-17 06:20:36 +08:00
|
|
|
read_lock_irq(&mapping->tree_lock);
|
|
|
|
|
|
|
|
/* Has the page been truncated while we slept? */
|
2006-01-06 16:11:08 +08:00
|
|
|
if (unlikely(page->mapping != mapping ||
|
|
|
|
page->index != offset)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
goto repeat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
read_unlock_irq(&mapping->tree_lock);
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(find_lock_page);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* find_or_create_page - locate or add a pagecache page
|
2005-05-01 23:59:26 +08:00
|
|
|
* @mapping: the page's address_space
|
|
|
|
* @index: the page's index into the mapping
|
|
|
|
* @gfp_mask: page allocation mode
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Locates a page in the pagecache. If the page is not present, a new page
|
|
|
|
* is allocated using @gfp_mask and is added to the pagecache and to the VM's
|
|
|
|
* LRU list. The returned page is locked and has its reference count
|
|
|
|
* incremented.
|
|
|
|
*
|
|
|
|
* find_or_create_page() may sleep, even if @gfp_flags specifies an atomic
|
|
|
|
* allocation!
|
|
|
|
*
|
|
|
|
* find_or_create_page() returns the desired page's address, or zero on
|
|
|
|
* memory exhaustion.
|
|
|
|
*/
|
|
|
|
struct page *find_or_create_page(struct address_space *mapping,
|
2005-10-21 15:18:50 +08:00
|
|
|
unsigned long index, gfp_t gfp_mask)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct page *page, *cached_page = NULL;
|
|
|
|
int err;
|
|
|
|
repeat:
|
|
|
|
page = find_lock_page(mapping, index);
|
|
|
|
if (!page) {
|
|
|
|
if (!cached_page) {
|
|
|
|
cached_page = alloc_page(gfp_mask);
|
|
|
|
if (!cached_page)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
err = add_to_page_cache_lru(cached_page, mapping,
|
|
|
|
index, gfp_mask);
|
|
|
|
if (!err) {
|
|
|
|
page = cached_page;
|
|
|
|
cached_page = NULL;
|
|
|
|
} else if (err == -EEXIST)
|
|
|
|
goto repeat;
|
|
|
|
}
|
|
|
|
if (cached_page)
|
|
|
|
page_cache_release(cached_page);
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(find_or_create_page);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* find_get_pages - gang pagecache lookup
|
|
|
|
* @mapping: The address_space to search
|
|
|
|
* @start: The starting page index
|
|
|
|
* @nr_pages: The maximum number of pages
|
|
|
|
* @pages: Where the resulting pages are placed
|
|
|
|
*
|
|
|
|
* find_get_pages() will search for and return a group of up to
|
|
|
|
* @nr_pages pages in the mapping. The pages are placed at @pages.
|
|
|
|
* find_get_pages() takes a reference against the returned pages.
|
|
|
|
*
|
|
|
|
* The search returns a group of mapping-contiguous pages with ascending
|
|
|
|
* indexes. There may be holes in the indices due to not-present pages.
|
|
|
|
*
|
|
|
|
* find_get_pages() returns the number of pages which were found.
|
|
|
|
*/
|
|
|
|
unsigned find_get_pages(struct address_space *mapping, pgoff_t start,
|
|
|
|
unsigned int nr_pages, struct page **pages)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
unsigned int ret;
|
|
|
|
|
|
|
|
read_lock_irq(&mapping->tree_lock);
|
|
|
|
ret = radix_tree_gang_lookup(&mapping->page_tree,
|
|
|
|
(void **)pages, start, nr_pages);
|
|
|
|
for (i = 0; i < ret; i++)
|
|
|
|
page_cache_get(pages[i]);
|
|
|
|
read_unlock_irq(&mapping->tree_lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-04-27 14:46:01 +08:00
|
|
|
/**
|
|
|
|
* find_get_pages_contig - gang contiguous pagecache lookup
|
|
|
|
* @mapping: The address_space to search
|
|
|
|
* @index: The starting page index
|
|
|
|
* @nr_pages: The maximum number of pages
|
|
|
|
* @pages: Where the resulting pages are placed
|
|
|
|
*
|
|
|
|
* find_get_pages_contig() works exactly like find_get_pages(), except
|
|
|
|
* that the returned number of pages are guaranteed to be contiguous.
|
|
|
|
*
|
|
|
|
* find_get_pages_contig() returns the number of pages which were found.
|
|
|
|
*/
|
|
|
|
unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t index,
|
|
|
|
unsigned int nr_pages, struct page **pages)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
unsigned int ret;
|
|
|
|
|
|
|
|
read_lock_irq(&mapping->tree_lock);
|
|
|
|
ret = radix_tree_gang_lookup(&mapping->page_tree,
|
|
|
|
(void **)pages, index, nr_pages);
|
|
|
|
for (i = 0; i < ret; i++) {
|
|
|
|
if (pages[i]->mapping == NULL || pages[i]->index != index)
|
|
|
|
break;
|
|
|
|
|
|
|
|
page_cache_get(pages[i]);
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
read_unlock_irq(&mapping->tree_lock);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* find_get_pages_tag - find and return pages that match @tag
|
|
|
|
* @mapping: the address_space to search
|
|
|
|
* @index: the starting page index
|
|
|
|
* @tag: the tag index
|
|
|
|
* @nr_pages: the maximum number of pages
|
|
|
|
* @pages: where the resulting pages are placed
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Like find_get_pages, except we only return pages which are tagged with
|
2006-06-23 17:03:49 +08:00
|
|
|
* @tag. We update @index to index the next page for the traversal.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index,
|
|
|
|
int tag, unsigned int nr_pages, struct page **pages)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
unsigned int ret;
|
|
|
|
|
|
|
|
read_lock_irq(&mapping->tree_lock);
|
|
|
|
ret = radix_tree_gang_lookup_tag(&mapping->page_tree,
|
|
|
|
(void **)pages, *index, nr_pages, tag);
|
|
|
|
for (i = 0; i < ret; i++)
|
|
|
|
page_cache_get(pages[i]);
|
|
|
|
if (ret)
|
|
|
|
*index = pages[ret - 1]->index + 1;
|
|
|
|
read_unlock_irq(&mapping->tree_lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* grab_cache_page_nowait - returns locked page at given index in given cache
|
|
|
|
* @mapping: target address_space
|
|
|
|
* @index: the page index
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Same as grab_cache_page, but do not wait if the page is unavailable.
|
|
|
|
* This is intended for speculative data generators, where the data can
|
|
|
|
* be regenerated if the page couldn't be grabbed. This routine should
|
|
|
|
* be safe to call while holding the lock for another page.
|
|
|
|
*
|
|
|
|
* Clear __GFP_FS when allocating the page to avoid recursion into the fs
|
|
|
|
* and deadlock against the caller's locked page.
|
|
|
|
*/
|
|
|
|
struct page *
|
|
|
|
grab_cache_page_nowait(struct address_space *mapping, unsigned long index)
|
|
|
|
{
|
|
|
|
struct page *page = find_get_page(mapping, index);
|
2005-10-21 15:18:50 +08:00
|
|
|
gfp_t gfp_mask;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (page) {
|
|
|
|
if (!TestSetPageLocked(page))
|
|
|
|
return page;
|
|
|
|
page_cache_release(page);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
gfp_mask = mapping_gfp_mask(mapping) & ~__GFP_FS;
|
|
|
|
page = alloc_pages(gfp_mask, 0);
|
|
|
|
if (page && add_to_page_cache_lru(page, mapping, index, gfp_mask)) {
|
|
|
|
page_cache_release(page);
|
|
|
|
page = NULL;
|
|
|
|
}
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(grab_cache_page_nowait);
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* do_generic_mapping_read - generic file read routine
|
|
|
|
* @mapping: address_space to be read
|
|
|
|
* @_ra: file's readahead state
|
|
|
|
* @filp: the file to read
|
|
|
|
* @ppos: current file position
|
|
|
|
* @desc: read_descriptor
|
|
|
|
* @actor: read method
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* This is a generic file read routine, and uses the
|
2006-06-23 17:03:49 +08:00
|
|
|
* mapping->a_ops->readpage() function for the actual low-level stuff.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* This is really ugly. But the goto's actually try to clarify some
|
|
|
|
* of the logic when it comes to error handling etc.
|
|
|
|
*
|
2006-06-23 17:03:49 +08:00
|
|
|
* Note the struct file* is only passed for the use of readpage.
|
|
|
|
* It may be NULL.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void do_generic_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;
|
|
|
|
unsigned long end_index;
|
|
|
|
unsigned long offset;
|
|
|
|
unsigned long last_index;
|
|
|
|
unsigned long next_index;
|
|
|
|
unsigned long prev_index;
|
|
|
|
loff_t isize;
|
|
|
|
struct page *cached_page;
|
|
|
|
int error;
|
|
|
|
struct file_ra_state ra = *_ra;
|
|
|
|
|
|
|
|
cached_page = NULL;
|
|
|
|
index = *ppos >> PAGE_CACHE_SHIFT;
|
|
|
|
next_index = index;
|
|
|
|
prev_index = ra.prev_page;
|
|
|
|
last_index = (*ppos + desc->count + PAGE_CACHE_SIZE-1) >> 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;
|
|
|
|
|
|
|
|
cond_resched();
|
|
|
|
if (index == next_index)
|
|
|
|
next_index = page_cache_readahead(mapping, &ra, filp,
|
|
|
|
index, last_index - index);
|
|
|
|
|
|
|
|
find_page:
|
|
|
|
page = find_get_page(mapping, index);
|
|
|
|
if (unlikely(page == NULL)) {
|
|
|
|
handle_ra_miss(mapping, &ra, index);
|
|
|
|
goto no_cached_page;
|
|
|
|
}
|
|
|
|
if (!PageUptodate(page))
|
|
|
|
goto page_not_up_to_date;
|
|
|
|
page_ok:
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When (part of) the same page is read multiple times
|
|
|
|
* in succession, only mark it as accessed the first time.
|
|
|
|
*/
|
|
|
|
if (prev_index != index)
|
|
|
|
mark_page_accessed(page);
|
|
|
|
prev_index = index;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
page_cache_release(page);
|
|
|
|
if (ret == nr && desc->count)
|
|
|
|
continue;
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
page_not_up_to_date:
|
|
|
|
/* Get exclusive access to the page ... */
|
|
|
|
lock_page(page);
|
|
|
|
|
|
|
|
/* Did it get unhashed before we got the lock? */
|
|
|
|
if (!page->mapping) {
|
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Did somebody else fill it already? */
|
|
|
|
if (PageUptodate(page)) {
|
|
|
|
unlock_page(page);
|
|
|
|
goto page_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
readpage:
|
|
|
|
/* Start the actual read. The read will unlock the page. */
|
|
|
|
error = mapping->a_ops->readpage(filp, page);
|
|
|
|
|
2005-12-16 06:28:17 +08:00
|
|
|
if (unlikely(error)) {
|
|
|
|
if (error == AOP_TRUNCATED_PAGE) {
|
|
|
|
page_cache_release(page);
|
|
|
|
goto find_page;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
goto readpage_error;
|
2005-12-16 06:28:17 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!PageUptodate(page)) {
|
|
|
|
lock_page(page);
|
|
|
|
if (!PageUptodate(page)) {
|
|
|
|
if (page->mapping == NULL) {
|
|
|
|
/*
|
|
|
|
* invalidate_inode_pages got it
|
|
|
|
*/
|
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
goto find_page;
|
|
|
|
}
|
|
|
|
unlock_page(page);
|
|
|
|
error = -EIO;
|
|
|
|
goto readpage_error;
|
|
|
|
}
|
|
|
|
unlock_page(page);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* i_size must be checked after we have done ->readpage.
|
|
|
|
*
|
|
|
|
* Checking i_size after the readpage allows us to calculate
|
|
|
|
* the correct value for "nr", which means the zero-filled
|
|
|
|
* part of the page is not copied back to userspace (unless
|
|
|
|
* another truncate extends the file - this is desired though).
|
|
|
|
*/
|
|
|
|
isize = i_size_read(inode);
|
|
|
|
end_index = (isize - 1) >> PAGE_CACHE_SHIFT;
|
|
|
|
if (unlikely(!isize || index > end_index)) {
|
|
|
|
page_cache_release(page);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* nr is the maximum number of bytes to copy from this page */
|
|
|
|
nr = PAGE_CACHE_SIZE;
|
|
|
|
if (index == end_index) {
|
|
|
|
nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1;
|
|
|
|
if (nr <= offset) {
|
|
|
|
page_cache_release(page);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nr = nr - offset;
|
|
|
|
goto page_ok;
|
|
|
|
|
|
|
|
readpage_error:
|
|
|
|
/* UHHUH! A synchronous read error occurred. Report it */
|
|
|
|
desc->error = error;
|
|
|
|
page_cache_release(page);
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
no_cached_page:
|
|
|
|
/*
|
|
|
|
* Ok, it wasn't cached, so we need to create a new
|
|
|
|
* page..
|
|
|
|
*/
|
|
|
|
if (!cached_page) {
|
|
|
|
cached_page = page_cache_alloc_cold(mapping);
|
|
|
|
if (!cached_page) {
|
|
|
|
desc->error = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
error = add_to_page_cache_lru(cached_page, mapping,
|
|
|
|
index, GFP_KERNEL);
|
|
|
|
if (error) {
|
|
|
|
if (error == -EEXIST)
|
|
|
|
goto find_page;
|
|
|
|
desc->error = error;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
page = cached_page;
|
|
|
|
cached_page = NULL;
|
|
|
|
goto readpage;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
*_ra = ra;
|
|
|
|
|
|
|
|
*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
|
|
|
|
if (cached_page)
|
|
|
|
page_cache_release(cached_page);
|
|
|
|
if (filp)
|
|
|
|
file_accessed(filp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(do_generic_mapping_read);
|
|
|
|
|
|
|
|
int file_read_actor(read_descriptor_t *desc, struct page *page,
|
|
|
|
unsigned long offset, unsigned long size)
|
|
|
|
{
|
|
|
|
char *kaddr;
|
|
|
|
unsigned long left, count = desc->count;
|
|
|
|
|
|
|
|
if (size > count)
|
|
|
|
size = count;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Faults on the destination of a read are common, so do it before
|
|
|
|
* taking the kmap.
|
|
|
|
*/
|
|
|
|
if (!fault_in_pages_writeable(desc->arg.buf, size)) {
|
|
|
|
kaddr = kmap_atomic(page, KM_USER0);
|
|
|
|
left = __copy_to_user_inatomic(desc->arg.buf,
|
|
|
|
kaddr + offset, size);
|
|
|
|
kunmap_atomic(kaddr, KM_USER0);
|
|
|
|
if (left == 0)
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do it the slow way */
|
|
|
|
kaddr = kmap(page);
|
|
|
|
left = __copy_to_user(desc->arg.buf, kaddr + offset, size);
|
|
|
|
kunmap(page);
|
|
|
|
|
|
|
|
if (left) {
|
|
|
|
size -= left;
|
|
|
|
desc->error = -EFAULT;
|
|
|
|
}
|
|
|
|
success:
|
|
|
|
desc->count = count - size;
|
|
|
|
desc->written += size;
|
|
|
|
desc->arg.buf += size;
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* __generic_file_aio_read - generic filesystem read routine
|
|
|
|
* @iocb: kernel I/O control block
|
|
|
|
* @iov: io vector request
|
|
|
|
* @nr_segs: number of segments in the iovec
|
|
|
|
* @ppos: current file position
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* This is the "read()" routine for all filesystems
|
|
|
|
* that can use the page cache directly.
|
|
|
|
*/
|
|
|
|
ssize_t
|
|
|
|
__generic_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
|
|
|
|
if (filp->f_flags & O_DIRECT) {
|
|
|
|
loff_t pos = *ppos, size;
|
|
|
|
struct address_space *mapping;
|
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
mapping = filp->f_mapping;
|
|
|
|
inode = mapping->host;
|
|
|
|
retval = 0;
|
|
|
|
if (!count)
|
|
|
|
goto out; /* skip atime */
|
|
|
|
size = i_size_read(inode);
|
|
|
|
if (pos < size) {
|
|
|
|
retval = generic_file_direct_IO(READ, iocb,
|
|
|
|
iov, pos, nr_segs);
|
[PATCH] fix for __generic_file_aio_read() to return 0 on EOF
I came across the following problem while running ltp-aiodio testcases from
ltp-full-20050405 on linux-2.6.12-rc3-mm3. I tried running the tests with
EXT3 as well as JFS filesystems.
One or two fsx-linux testcases were hung after some time. These testcases
were hanging at wait_for_all_aios().
Debugging shows that there were some iocbs which were not getting completed
eventhough the last retry for those returned -EIOCBQUEUED. Also all such
pending iocbs represented READ operation.
Further debugging revealed that all such iocbs hit EOF in the DIO layer.
To be more precise, the "pos" from which they were trying to read was
greater than the "size" of the file. So the generic_file_direct_IO
returned 0.
This happens rarely as there is already a check in
__generic_file_aio_read(), for whether "pos" < "size" before calling direct
IO routine.
>size = i_size_read(inode);
>if (pos < size) {
> retval = generic_file_direct_IO(READ, iocb,
> iov, pos, nr_segs);
But for READ, we are taking the inode->i_sem only in the DIO layer. So it
is possible that some other process can change the size of the file before
we take the i_sem. In such a case ( when "pos" > "size"), the
__generic_file_aio_read() would return -EIOCBQUEUED even though there were
no I/O requests submitted by the DIO layer. This would cause the AIO layer
to expect aio_complete() for THE iocb, which doesnot happen. And thus the
test hangs forever, waiting for an I/O completion, where there are no
requests submitted at all.
The following patch makes __generic_file_aio_read() return 0 (instead of
returning -EIOCBQUEUED), on getting 0 from generic_file_direct_IO(), so
that the AIO layer does the aio_complete().
Testing:
I have tested the patch on a SMP machine(with 2 Pentium 4 (HT)) running
linux-2.6.12-rc3-mm3. I ran the ltp-aiodio testcases and none of the
fsx-linux tests hung. Also the aio-stress tests ran without any problem.
Signed-off-by: Suzuki K P <suzuki@in.ibm.com>
Signed-off-by: Suparna Bhattacharya <suparna@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-05-22 07:33:36 +08:00
|
|
|
if (retval > 0 && !is_sync_kiocb(iocb))
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = -EIOCBQUEUED;
|
|
|
|
if (retval > 0)
|
|
|
|
*ppos = pos + retval;
|
|
|
|
}
|
|
|
|
file_accessed(filp);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
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_generic_file_read(filp,ppos,&desc,file_read_actor);
|
|
|
|
retval += desc.written;
|
[PATCH] fs: error case fix in __generic_file_aio_read
When __generic_file_aio_read() hits an error during reading, it reports the
error iff nothing has successfully been read yet. This is condition - when
an error occurs, if nothing has been read/written, report the error code;
otherwise, report the amount of bytes successfully transferred upto that
point.
This corner case can be exposed by performing readv(2) with the following
iov.
iov[0] = len0 @ ptr0
iov[1] = len1 @ NULL (or any other invalid pointer)
iov[2] = len2 @ ptr2
When file size is enough, performing above readv(2) results in
len0 bytes from file_pos @ ptr0
len2 bytes from file_pos + len0 @ ptr2
And the return value is len0 + len2. Test program is attached to this
mail.
This patch makes __generic_file_aio_read()'s error handling identical to
other functions.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
#include <errno.h>
#include <string.h>
int main(int argc, char **argv)
{
const char *path;
struct stat stbuf;
size_t len0, len1;
void *buf0, *buf1;
struct iovec iov[3];
int fd, i;
ssize_t ret;
if (argc < 2) {
fprintf(stderr, "Usage: testreadv path (better be a "
"small text file)\n");
return 1;
}
path = argv[1];
if (stat(path, &stbuf) < 0) {
perror("stat");
return 1;
}
len0 = stbuf.st_size / 2;
len1 = stbuf.st_size - len0;
if (!len0 || !len1) {
fprintf(stderr, "Dude, file is too small\n");
return 1;
}
if ((fd = open(path, O_RDONLY)) < 0) {
perror("open");
return 1;
}
if (!(buf0 = malloc(len0)) || !(buf1 = malloc(len1))) {
perror("malloc");
return 1;
}
memset(buf0, 0, len0);
memset(buf1, 0, len1);
iov[0].iov_base = buf0;
iov[0].iov_len = len0;
iov[1].iov_base = NULL;
iov[1].iov_len = len1;
iov[2].iov_base = buf1;
iov[2].iov_len = len1;
printf("vector ");
for (i = 0; i < 3; i++)
printf("%p:%zu ", iov[i].iov_base, iov[i].iov_len);
printf("\n");
ret = readv(fd, iov, 3);
if (ret < 0)
perror("readv");
printf("readv returned %zd\nbuf0 = [%s]\nbuf1 = [%s]\n",
ret, (char *)buf0, (char *)buf1);
return 0;
}
Signed-off-by: Tejun Heo <htejun@gmail.com>
Cc: Benjamin LaHaise <bcrl@kvack.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-31 07:02:40 +08:00
|
|
|
if (desc.error) {
|
|
|
|
retval = retval ?: desc.error;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__generic_file_aio_read);
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
generic_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 __generic_file_aio_read(iocb, &local_iov, 1, &iocb->ki_pos);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_aio_read);
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
generic_file_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct iovec local_iov = { .iov_base = buf, .iov_len = count };
|
|
|
|
struct kiocb kiocb;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
init_sync_kiocb(&kiocb, filp);
|
|
|
|
ret = __generic_file_aio_read(&kiocb, &local_iov, 1, ppos);
|
|
|
|
if (-EIOCBQUEUED == ret)
|
|
|
|
ret = wait_on_sync_kiocb(&kiocb);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_read);
|
|
|
|
|
|
|
|
int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size)
|
|
|
|
{
|
|
|
|
ssize_t written;
|
|
|
|
unsigned long count = desc->count;
|
|
|
|
struct file *file = desc->arg.data;
|
|
|
|
|
|
|
|
if (size > count)
|
|
|
|
size = count;
|
|
|
|
|
|
|
|
written = file->f_op->sendpage(file, page, offset,
|
|
|
|
size, &file->f_pos, size<count);
|
|
|
|
if (written < 0) {
|
|
|
|
desc->error = written;
|
|
|
|
written = 0;
|
|
|
|
}
|
|
|
|
desc->count = count - written;
|
|
|
|
desc->written += written;
|
|
|
|
return written;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t generic_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_generic_file_read(in_file, ppos, &desc, actor);
|
|
|
|
if (desc.written)
|
|
|
|
return desc.written;
|
|
|
|
return desc.error;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_sendfile);
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
do_readahead(struct address_space *mapping, struct file *filp,
|
|
|
|
unsigned long index, unsigned long nr)
|
|
|
|
{
|
|
|
|
if (!mapping || !mapping->a_ops || !mapping->a_ops->readpage)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
force_page_cache_readahead(mapping, filp, index,
|
|
|
|
max_sane_readahead(nr));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
asmlinkage ssize_t sys_readahead(int fd, loff_t offset, size_t count)
|
|
|
|
{
|
|
|
|
ssize_t ret;
|
|
|
|
struct file *file;
|
|
|
|
|
|
|
|
ret = -EBADF;
|
|
|
|
file = fget(fd);
|
|
|
|
if (file) {
|
|
|
|
if (file->f_mode & FMODE_READ) {
|
|
|
|
struct address_space *mapping = file->f_mapping;
|
|
|
|
unsigned long start = offset >> PAGE_CACHE_SHIFT;
|
|
|
|
unsigned long end = (offset + count - 1) >> PAGE_CACHE_SHIFT;
|
|
|
|
unsigned long len = end - start + 1;
|
|
|
|
ret = do_readahead(mapping, file, start, len);
|
|
|
|
}
|
|
|
|
fput(file);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_MMU
|
2006-06-23 17:03:49 +08:00
|
|
|
static int FASTCALL(page_cache_read(struct file * file, unsigned long offset));
|
|
|
|
/**
|
|
|
|
* page_cache_read - adds requested page to the page cache if not already there
|
|
|
|
* @file: file to read
|
|
|
|
* @offset: page index
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* This adds the requested page to the page cache if it isn't already there,
|
|
|
|
* and schedules an I/O to read in its contents from disk.
|
|
|
|
*/
|
|
|
|
static int fastcall page_cache_read(struct file * file, unsigned long offset)
|
|
|
|
{
|
|
|
|
struct address_space *mapping = file->f_mapping;
|
|
|
|
struct page *page;
|
2005-12-16 06:28:17 +08:00
|
|
|
int ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-12-16 06:28:17 +08:00
|
|
|
do {
|
|
|
|
page = page_cache_alloc_cold(mapping);
|
|
|
|
if (!page)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = add_to_page_cache_lru(page, mapping, offset, GFP_KERNEL);
|
|
|
|
if (ret == 0)
|
|
|
|
ret = mapping->a_ops->readpage(file, page);
|
|
|
|
else if (ret == -EEXIST)
|
|
|
|
ret = 0; /* losing race to add is OK */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
page_cache_release(page);
|
|
|
|
|
2005-12-16 06:28:17 +08:00
|
|
|
} while (ret == AOP_TRUNCATED_PAGE);
|
|
|
|
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#define MMAP_LOTSAMISS (100)
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* filemap_nopage - read in file data for page fault handling
|
|
|
|
* @area: the applicable vm_area
|
|
|
|
* @address: target address to read in
|
|
|
|
* @type: returned with VM_FAULT_{MINOR,MAJOR} if not %NULL
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* filemap_nopage() is invoked via the vma operations vector for a
|
|
|
|
* mapped memory region to read in file data during a page fault.
|
|
|
|
*
|
|
|
|
* The goto's are kind of ugly, but this streamlines the normal case of having
|
|
|
|
* it in the page cache, and handles the special cases reasonably without
|
|
|
|
* having a lot of duplicated code.
|
|
|
|
*/
|
|
|
|
struct page *filemap_nopage(struct vm_area_struct *area,
|
|
|
|
unsigned long address, int *type)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
struct file *file = area->vm_file;
|
|
|
|
struct address_space *mapping = file->f_mapping;
|
|
|
|
struct file_ra_state *ra = &file->f_ra;
|
|
|
|
struct inode *inode = mapping->host;
|
|
|
|
struct page *page;
|
|
|
|
unsigned long size, pgoff;
|
|
|
|
int did_readaround = 0, majmin = VM_FAULT_MINOR;
|
|
|
|
|
|
|
|
pgoff = ((address-area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
|
|
|
|
|
|
|
|
retry_all:
|
|
|
|
size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
|
|
|
if (pgoff >= size)
|
|
|
|
goto outside_data_content;
|
|
|
|
|
|
|
|
/* If we don't want any read-ahead, don't bother */
|
|
|
|
if (VM_RandomReadHint(area))
|
|
|
|
goto no_cached_page;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The readahead code wants to be told about each and every page
|
|
|
|
* so it can build and shrink its windows appropriately
|
|
|
|
*
|
|
|
|
* For sequential accesses, we use the generic readahead logic.
|
|
|
|
*/
|
|
|
|
if (VM_SequentialReadHint(area))
|
|
|
|
page_cache_readahead(mapping, ra, file, pgoff, 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we have something in the page cache already?
|
|
|
|
*/
|
|
|
|
retry_find:
|
|
|
|
page = find_get_page(mapping, pgoff);
|
|
|
|
if (!page) {
|
|
|
|
unsigned long ra_pages;
|
|
|
|
|
|
|
|
if (VM_SequentialReadHint(area)) {
|
|
|
|
handle_ra_miss(mapping, ra, pgoff);
|
|
|
|
goto no_cached_page;
|
|
|
|
}
|
|
|
|
ra->mmap_miss++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we miss much more than hit in this file? If so,
|
|
|
|
* stop bothering with read-ahead. It will only hurt.
|
|
|
|
*/
|
|
|
|
if (ra->mmap_miss > ra->mmap_hit + MMAP_LOTSAMISS)
|
|
|
|
goto no_cached_page;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* To keep the pgmajfault counter straight, we need to
|
|
|
|
* check did_readaround, as this is an inner loop.
|
|
|
|
*/
|
|
|
|
if (!did_readaround) {
|
|
|
|
majmin = VM_FAULT_MAJOR;
|
|
|
|
inc_page_state(pgmajfault);
|
|
|
|
}
|
|
|
|
did_readaround = 1;
|
|
|
|
ra_pages = max_sane_readahead(file->f_ra.ra_pages);
|
|
|
|
if (ra_pages) {
|
|
|
|
pgoff_t start = 0;
|
|
|
|
|
|
|
|
if (pgoff > ra_pages / 2)
|
|
|
|
start = pgoff - ra_pages / 2;
|
|
|
|
do_page_cache_readahead(mapping, file, start, ra_pages);
|
|
|
|
}
|
|
|
|
page = find_get_page(mapping, pgoff);
|
|
|
|
if (!page)
|
|
|
|
goto no_cached_page;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!did_readaround)
|
|
|
|
ra->mmap_hit++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, found a page in the page cache, now we need to check
|
|
|
|
* that it's up-to-date.
|
|
|
|
*/
|
|
|
|
if (!PageUptodate(page))
|
|
|
|
goto page_not_uptodate;
|
|
|
|
|
|
|
|
success:
|
|
|
|
/*
|
|
|
|
* Found the page and have a reference on it.
|
|
|
|
*/
|
|
|
|
mark_page_accessed(page);
|
|
|
|
if (type)
|
|
|
|
*type = majmin;
|
|
|
|
return page;
|
|
|
|
|
|
|
|
outside_data_content:
|
|
|
|
/*
|
|
|
|
* An external ptracer can access pages that normally aren't
|
|
|
|
* accessible..
|
|
|
|
*/
|
|
|
|
if (area->vm_mm == current->mm)
|
|
|
|
return NULL;
|
|
|
|
/* Fall through to the non-read-ahead case */
|
|
|
|
no_cached_page:
|
|
|
|
/*
|
|
|
|
* We're only likely to ever get here if MADV_RANDOM is in
|
|
|
|
* effect.
|
|
|
|
*/
|
|
|
|
error = page_cache_read(file, pgoff);
|
|
|
|
grab_swap_token();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The page we want has now been added to the page cache.
|
|
|
|
* In the unlikely event that someone removed it in the
|
|
|
|
* meantime, we'll just come back here and read it again.
|
|
|
|
*/
|
|
|
|
if (error >= 0)
|
|
|
|
goto retry_find;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* An error return from page_cache_read can result if the
|
|
|
|
* system is low on memory, or a problem occurs while trying
|
|
|
|
* to schedule I/O.
|
|
|
|
*/
|
|
|
|
if (error == -ENOMEM)
|
|
|
|
return NOPAGE_OOM;
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
page_not_uptodate:
|
|
|
|
if (!did_readaround) {
|
|
|
|
majmin = VM_FAULT_MAJOR;
|
|
|
|
inc_page_state(pgmajfault);
|
|
|
|
}
|
|
|
|
lock_page(page);
|
|
|
|
|
|
|
|
/* Did it get unhashed while we waited for it? */
|
|
|
|
if (!page->mapping) {
|
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
goto retry_all;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Did somebody else get it up-to-date? */
|
|
|
|
if (PageUptodate(page)) {
|
|
|
|
unlock_page(page);
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:28:17 +08:00
|
|
|
error = mapping->a_ops->readpage(file, page);
|
|
|
|
if (!error) {
|
2005-04-17 06:20:36 +08:00
|
|
|
wait_on_page_locked(page);
|
|
|
|
if (PageUptodate(page))
|
|
|
|
goto success;
|
2005-12-16 06:28:17 +08:00
|
|
|
} else if (error == AOP_TRUNCATED_PAGE) {
|
|
|
|
page_cache_release(page);
|
|
|
|
goto retry_find;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Umm, take care of errors if the page isn't up-to-date.
|
|
|
|
* Try to re-read it _once_. We do this synchronously,
|
|
|
|
* because there really aren't any performance issues here
|
|
|
|
* and we need to check for errors.
|
|
|
|
*/
|
|
|
|
lock_page(page);
|
|
|
|
|
|
|
|
/* Somebody truncated the page on us? */
|
|
|
|
if (!page->mapping) {
|
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
goto retry_all;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Somebody else successfully read it in? */
|
|
|
|
if (PageUptodate(page)) {
|
|
|
|
unlock_page(page);
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
ClearPageError(page);
|
2005-12-16 06:28:17 +08:00
|
|
|
error = mapping->a_ops->readpage(file, page);
|
|
|
|
if (!error) {
|
2005-04-17 06:20:36 +08:00
|
|
|
wait_on_page_locked(page);
|
|
|
|
if (PageUptodate(page))
|
|
|
|
goto success;
|
2005-12-16 06:28:17 +08:00
|
|
|
} else if (error == AOP_TRUNCATED_PAGE) {
|
|
|
|
page_cache_release(page);
|
|
|
|
goto retry_find;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Things didn't work out. Return zero to tell the
|
|
|
|
* mm layer so, possibly freeing the page cache page first.
|
|
|
|
*/
|
|
|
|
page_cache_release(page);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(filemap_nopage);
|
|
|
|
|
|
|
|
static struct page * filemap_getpage(struct file *file, unsigned long pgoff,
|
|
|
|
int nonblock)
|
|
|
|
{
|
|
|
|
struct address_space *mapping = file->f_mapping;
|
|
|
|
struct page *page;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we have something in the page cache already?
|
|
|
|
*/
|
|
|
|
retry_find:
|
|
|
|
page = find_get_page(mapping, pgoff);
|
|
|
|
if (!page) {
|
|
|
|
if (nonblock)
|
|
|
|
return NULL;
|
|
|
|
goto no_cached_page;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, found a page in the page cache, now we need to check
|
|
|
|
* that it's up-to-date.
|
|
|
|
*/
|
2005-04-17 06:24:05 +08:00
|
|
|
if (!PageUptodate(page)) {
|
|
|
|
if (nonblock) {
|
|
|
|
page_cache_release(page);
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
goto page_not_uptodate;
|
2005-04-17 06:24:05 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
success:
|
|
|
|
/*
|
|
|
|
* Found the page and have a reference on it.
|
|
|
|
*/
|
|
|
|
mark_page_accessed(page);
|
|
|
|
return page;
|
|
|
|
|
|
|
|
no_cached_page:
|
|
|
|
error = page_cache_read(file, pgoff);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The page we want has now been added to the page cache.
|
|
|
|
* In the unlikely event that someone removed it in the
|
|
|
|
* meantime, we'll just come back here and read it again.
|
|
|
|
*/
|
|
|
|
if (error >= 0)
|
|
|
|
goto retry_find;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* An error return from page_cache_read can result if the
|
|
|
|
* system is low on memory, or a problem occurs while trying
|
|
|
|
* to schedule I/O.
|
|
|
|
*/
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
page_not_uptodate:
|
|
|
|
lock_page(page);
|
|
|
|
|
|
|
|
/* Did it get unhashed while we waited for it? */
|
|
|
|
if (!page->mapping) {
|
|
|
|
unlock_page(page);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Did somebody else get it up-to-date? */
|
|
|
|
if (PageUptodate(page)) {
|
|
|
|
unlock_page(page);
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
|
2005-12-16 06:28:17 +08:00
|
|
|
error = mapping->a_ops->readpage(file, page);
|
|
|
|
if (!error) {
|
2005-04-17 06:20:36 +08:00
|
|
|
wait_on_page_locked(page);
|
|
|
|
if (PageUptodate(page))
|
|
|
|
goto success;
|
2005-12-16 06:28:17 +08:00
|
|
|
} else if (error == AOP_TRUNCATED_PAGE) {
|
|
|
|
page_cache_release(page);
|
|
|
|
goto retry_find;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Umm, take care of errors if the page isn't up-to-date.
|
|
|
|
* Try to re-read it _once_. We do this synchronously,
|
|
|
|
* because there really aren't any performance issues here
|
|
|
|
* and we need to check for errors.
|
|
|
|
*/
|
|
|
|
lock_page(page);
|
|
|
|
|
|
|
|
/* Somebody truncated the page on us? */
|
|
|
|
if (!page->mapping) {
|
|
|
|
unlock_page(page);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Somebody else successfully read it in? */
|
|
|
|
if (PageUptodate(page)) {
|
|
|
|
unlock_page(page);
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
|
|
|
|
ClearPageError(page);
|
2005-12-16 06:28:17 +08:00
|
|
|
error = mapping->a_ops->readpage(file, page);
|
|
|
|
if (!error) {
|
2005-04-17 06:20:36 +08:00
|
|
|
wait_on_page_locked(page);
|
|
|
|
if (PageUptodate(page))
|
|
|
|
goto success;
|
2005-12-16 06:28:17 +08:00
|
|
|
} else if (error == AOP_TRUNCATED_PAGE) {
|
|
|
|
page_cache_release(page);
|
|
|
|
goto retry_find;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Things didn't work out. Return zero to tell the
|
|
|
|
* mm layer so, possibly freeing the page cache page first.
|
|
|
|
*/
|
|
|
|
err:
|
|
|
|
page_cache_release(page);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int filemap_populate(struct vm_area_struct *vma, unsigned long addr,
|
|
|
|
unsigned long len, pgprot_t prot, unsigned long pgoff,
|
|
|
|
int nonblock)
|
|
|
|
{
|
|
|
|
struct file *file = vma->vm_file;
|
|
|
|
struct address_space *mapping = file->f_mapping;
|
|
|
|
struct inode *inode = mapping->host;
|
|
|
|
unsigned long size;
|
|
|
|
struct mm_struct *mm = vma->vm_mm;
|
|
|
|
struct page *page;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!nonblock)
|
|
|
|
force_page_cache_readahead(mapping, vma->vm_file,
|
|
|
|
pgoff, len >> PAGE_CACHE_SHIFT);
|
|
|
|
|
|
|
|
repeat:
|
|
|
|
size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
|
|
|
if (pgoff + (len >> PAGE_CACHE_SHIFT) > size)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
page = filemap_getpage(file, pgoff, nonblock);
|
2005-09-04 06:54:55 +08:00
|
|
|
|
|
|
|
/* XXX: This is wrong, a filesystem I/O error may have happened. Fix that as
|
|
|
|
* done in shmem_populate calling shmem_getpage */
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!page && !nonblock)
|
|
|
|
return -ENOMEM;
|
2005-09-04 06:54:55 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (page) {
|
|
|
|
err = install_page(mm, vma, addr, page, prot);
|
|
|
|
if (err) {
|
|
|
|
page_cache_release(page);
|
|
|
|
return err;
|
|
|
|
}
|
[PATCH] mm: page fault handlers tidyup
Impose a little more consistency on the page fault handlers do_wp_page,
do_swap_page, do_anonymous_page, do_no_page, do_file_page: why not pass their
arguments in the same order, called the same names?
break_cow is all very well, but what it did was inlined elsewhere: easier to
compare if it's brought back into do_wp_page.
do_file_page's fallback to do_no_page dates from a time when we were testing
pte_file by using it wherever possible: currently it's peculiar to nonlinear
vmas, so just check that. BUG_ON if not? Better not, it's probably page
table corruption, so just show the pte: hmm, there's a pte_ERROR macro, let's
use that for do_wp_page's invalid pfn too.
Hah! Someone in the ppc64 world noticed pte_ERROR was unused so removed it:
restored (and say "pud" not "pmd" in its pud_ERROR).
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-30 09:15:59 +08:00
|
|
|
} else if (vma->vm_flags & VM_NONLINEAR) {
|
2005-09-04 06:54:55 +08:00
|
|
|
/* No page was found just because we can't read it in now (being
|
|
|
|
* here implies nonblock != 0), but the page may exist, so set
|
|
|
|
* the PTE to fault it in later. */
|
2005-04-17 06:20:36 +08:00
|
|
|
err = install_file_pte(mm, vma, addr, pgoff, prot);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
len -= PAGE_SIZE;
|
|
|
|
addr += PAGE_SIZE;
|
|
|
|
pgoff++;
|
|
|
|
if (len)
|
|
|
|
goto repeat;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2005-10-30 09:17:02 +08:00
|
|
|
EXPORT_SYMBOL(filemap_populate);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
struct vm_operations_struct generic_file_vm_ops = {
|
|
|
|
.nopage = filemap_nopage,
|
|
|
|
.populate = filemap_populate,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* This is used for a general mmap of a disk file */
|
|
|
|
|
|
|
|
int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
|
|
|
|
{
|
|
|
|
struct address_space *mapping = file->f_mapping;
|
|
|
|
|
|
|
|
if (!mapping->a_ops->readpage)
|
|
|
|
return -ENOEXEC;
|
|
|
|
file_accessed(file);
|
|
|
|
vma->vm_ops = &generic_file_vm_ops;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is for filesystems which do not implement ->writepage.
|
|
|
|
*/
|
|
|
|
int generic_file_readonly_mmap(struct file *file, struct vm_area_struct *vma)
|
|
|
|
{
|
|
|
|
if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
|
|
|
|
return -EINVAL;
|
|
|
|
return generic_file_mmap(file, vma);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
|
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
int generic_file_readonly_mmap(struct file * file, struct vm_area_struct * vma)
|
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_MMU */
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(generic_file_mmap);
|
|
|
|
EXPORT_SYMBOL(generic_file_readonly_mmap);
|
|
|
|
|
|
|
|
static inline struct page *__read_cache_page(struct address_space *mapping,
|
|
|
|
unsigned long index,
|
|
|
|
int (*filler)(void *,struct page*),
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct page *page, *cached_page = NULL;
|
|
|
|
int err;
|
|
|
|
repeat:
|
|
|
|
page = find_get_page(mapping, index);
|
|
|
|
if (!page) {
|
|
|
|
if (!cached_page) {
|
|
|
|
cached_page = page_cache_alloc_cold(mapping);
|
|
|
|
if (!cached_page)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
}
|
|
|
|
err = add_to_page_cache_lru(cached_page, mapping,
|
|
|
|
index, GFP_KERNEL);
|
|
|
|
if (err == -EEXIST)
|
|
|
|
goto repeat;
|
|
|
|
if (err < 0) {
|
|
|
|
/* Presumably ENOMEM for radix tree node */
|
|
|
|
page_cache_release(cached_page);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
page = cached_page;
|
|
|
|
cached_page = NULL;
|
|
|
|
err = filler(data, page);
|
|
|
|
if (err < 0) {
|
|
|
|
page_cache_release(page);
|
|
|
|
page = ERR_PTR(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cached_page)
|
|
|
|
page_cache_release(cached_page);
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
2006-06-23 17:03:49 +08:00
|
|
|
/**
|
|
|
|
* read_cache_page - read into page cache, fill it if needed
|
|
|
|
* @mapping: the page's address_space
|
|
|
|
* @index: the page index
|
|
|
|
* @filler: function to perform the read
|
|
|
|
* @data: destination for read data
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Read into the page cache. If a page already exists,
|
|
|
|
* and PageUptodate() is not set, try to fill the page.
|
|
|
|
*/
|
|
|
|
struct page *read_cache_page(struct address_space *mapping,
|
|
|
|
unsigned long index,
|
|
|
|
int (*filler)(void *,struct page*),
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct page *page;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
retry:
|
|
|
|
page = __read_cache_page(mapping, index, filler, data);
|
|
|
|
if (IS_ERR(page))
|
|
|
|
goto out;
|
|
|
|
mark_page_accessed(page);
|
|
|
|
if (PageUptodate(page))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
lock_page(page);
|
|
|
|
if (!page->mapping) {
|
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
if (PageUptodate(page)) {
|
|
|
|
unlock_page(page);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
err = filler(data, page);
|
|
|
|
if (err < 0) {
|
|
|
|
page_cache_release(page);
|
|
|
|
page = ERR_PTR(err);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(read_cache_page);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the page was newly created, increment its refcount and add it to the
|
|
|
|
* caller's lru-buffering pagevec. This function is specifically for
|
|
|
|
* generic_file_write().
|
|
|
|
*/
|
|
|
|
static inline struct page *
|
|
|
|
__grab_cache_page(struct address_space *mapping, unsigned long index,
|
|
|
|
struct page **cached_page, struct pagevec *lru_pvec)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct page *page;
|
|
|
|
repeat:
|
|
|
|
page = find_lock_page(mapping, index);
|
|
|
|
if (!page) {
|
|
|
|
if (!*cached_page) {
|
|
|
|
*cached_page = page_cache_alloc(mapping);
|
|
|
|
if (!*cached_page)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
err = add_to_page_cache(*cached_page, mapping,
|
|
|
|
index, GFP_KERNEL);
|
|
|
|
if (err == -EEXIST)
|
|
|
|
goto repeat;
|
|
|
|
if (err == 0) {
|
|
|
|
page = *cached_page;
|
|
|
|
page_cache_get(page);
|
|
|
|
if (!pagevec_add(lru_pvec, page))
|
|
|
|
__pagevec_lru_add(lru_pvec);
|
|
|
|
*cached_page = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The logic we want is
|
|
|
|
*
|
|
|
|
* if suid or (sgid and xgrp)
|
|
|
|
* remove privs
|
|
|
|
*/
|
|
|
|
int remove_suid(struct dentry *dentry)
|
|
|
|
{
|
|
|
|
mode_t mode = dentry->d_inode->i_mode;
|
|
|
|
int kill = 0;
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
/* suid always must be killed */
|
|
|
|
if (unlikely(mode & S_ISUID))
|
|
|
|
kill = ATTR_KILL_SUID;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sgid without any exec bits is just a mandatory locking mark; leave
|
|
|
|
* it alone. If some exec bits are set, it's a real sgid; kill it.
|
|
|
|
*/
|
|
|
|
if (unlikely((mode & S_ISGID) && (mode & S_IXGRP)))
|
|
|
|
kill |= ATTR_KILL_SGID;
|
|
|
|
|
|
|
|
if (unlikely(kill && !capable(CAP_FSETID))) {
|
|
|
|
struct iattr newattrs;
|
|
|
|
|
|
|
|
newattrs.ia_valid = ATTR_FORCE | kill;
|
|
|
|
result = notify_change(dentry, &newattrs);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(remove_suid);
|
|
|
|
|
2005-06-24 13:05:25 +08:00
|
|
|
size_t
|
2005-04-17 06:20:36 +08:00
|
|
|
__filemap_copy_from_user_iovec(char *vaddr,
|
|
|
|
const struct iovec *iov, size_t base, size_t bytes)
|
|
|
|
{
|
|
|
|
size_t copied = 0, left = 0;
|
|
|
|
|
|
|
|
while (bytes) {
|
|
|
|
char __user *buf = iov->iov_base + base;
|
|
|
|
int copy = min(bytes, iov->iov_len - base);
|
|
|
|
|
|
|
|
base = 0;
|
2006-06-23 17:04:16 +08:00
|
|
|
left = __copy_from_user_inatomic_nocache(vaddr, buf, copy);
|
2005-04-17 06:20:36 +08:00
|
|
|
copied += copy;
|
|
|
|
bytes -= copy;
|
|
|
|
vaddr += copy;
|
|
|
|
iov++;
|
|
|
|
|
|
|
|
if (unlikely(left)) {
|
|
|
|
/* zero the rest of the target like __copy_from_user */
|
|
|
|
if (bytes)
|
|
|
|
memset(vaddr, 0, bytes);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return copied - left;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Performs necessary checks before doing a write
|
|
|
|
*
|
2006-06-23 17:03:49 +08:00
|
|
|
* Can adjust writing position or amount of bytes to write.
|
2005-04-17 06:20:36 +08:00
|
|
|
* Returns appropriate error code that caller should return or
|
|
|
|
* zero in case that write should be allowed.
|
|
|
|
*/
|
|
|
|
inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk)
|
|
|
|
{
|
|
|
|
struct inode *inode = file->f_mapping->host;
|
|
|
|
unsigned long limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
|
|
|
|
|
|
|
|
if (unlikely(*pos < 0))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!isblk) {
|
|
|
|
/* FIXME: this is for backwards compatibility with 2.4 */
|
|
|
|
if (file->f_flags & O_APPEND)
|
|
|
|
*pos = i_size_read(inode);
|
|
|
|
|
|
|
|
if (limit != RLIM_INFINITY) {
|
|
|
|
if (*pos >= limit) {
|
|
|
|
send_sig(SIGXFSZ, current, 0);
|
|
|
|
return -EFBIG;
|
|
|
|
}
|
|
|
|
if (*count > limit - (typeof(limit))*pos) {
|
|
|
|
*count = limit - (typeof(limit))*pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LFS rule
|
|
|
|
*/
|
|
|
|
if (unlikely(*pos + *count > MAX_NON_LFS &&
|
|
|
|
!(file->f_flags & O_LARGEFILE))) {
|
|
|
|
if (*pos >= MAX_NON_LFS) {
|
|
|
|
send_sig(SIGXFSZ, current, 0);
|
|
|
|
return -EFBIG;
|
|
|
|
}
|
|
|
|
if (*count > MAX_NON_LFS - (unsigned long)*pos) {
|
|
|
|
*count = MAX_NON_LFS - (unsigned long)*pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Are we about to exceed the fs block limit ?
|
|
|
|
*
|
|
|
|
* If we have written data it becomes a short write. If we have
|
|
|
|
* exceeded without writing data we send a signal and return EFBIG.
|
|
|
|
* Linus frestrict idea will clean these up nicely..
|
|
|
|
*/
|
|
|
|
if (likely(!isblk)) {
|
|
|
|
if (unlikely(*pos >= inode->i_sb->s_maxbytes)) {
|
|
|
|
if (*count || *pos > inode->i_sb->s_maxbytes) {
|
|
|
|
send_sig(SIGXFSZ, current, 0);
|
|
|
|
return -EFBIG;
|
|
|
|
}
|
|
|
|
/* zero-length writes at ->s_maxbytes are OK */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlikely(*pos + *count > inode->i_sb->s_maxbytes))
|
|
|
|
*count = inode->i_sb->s_maxbytes - *pos;
|
|
|
|
} else {
|
|
|
|
loff_t isize;
|
|
|
|
if (bdev_read_only(I_BDEV(inode)))
|
|
|
|
return -EPERM;
|
|
|
|
isize = i_size_read(inode);
|
|
|
|
if (*pos >= isize) {
|
|
|
|
if (*count || *pos > isize)
|
|
|
|
return -ENOSPC;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*pos + *count > isize)
|
|
|
|
*count = isize - *pos;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_write_checks);
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
|
|
|
|
unsigned long *nr_segs, loff_t pos, loff_t *ppos,
|
|
|
|
size_t count, size_t ocount)
|
|
|
|
{
|
|
|
|
struct file *file = iocb->ki_filp;
|
|
|
|
struct address_space *mapping = file->f_mapping;
|
|
|
|
struct inode *inode = mapping->host;
|
|
|
|
ssize_t written;
|
|
|
|
|
|
|
|
if (count != ocount)
|
|
|
|
*nr_segs = iov_shorten((struct iovec *)iov, *nr_segs, count);
|
|
|
|
|
|
|
|
written = generic_file_direct_IO(WRITE, iocb, iov, pos, *nr_segs);
|
|
|
|
if (written > 0) {
|
|
|
|
loff_t end = pos + written;
|
|
|
|
if (end > i_size_read(inode) && !S_ISBLK(inode->i_mode)) {
|
|
|
|
i_size_write(inode, end);
|
|
|
|
mark_inode_dirty(inode);
|
|
|
|
}
|
|
|
|
*ppos = end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sync the fs metadata but not the minor inode changes and
|
|
|
|
* of course not the data as we did direct DMA for the IO.
|
2006-01-10 07:59:24 +08:00
|
|
|
* i_mutex is held, which protects generic_osync_inode() from
|
2005-04-17 06:20:36 +08:00
|
|
|
* livelocking.
|
|
|
|
*/
|
2005-06-26 05:54:32 +08:00
|
|
|
if (written >= 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
|
|
|
|
int err = generic_osync_inode(inode, mapping, OSYNC_METADATA);
|
|
|
|
if (err < 0)
|
|
|
|
written = err;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
if (written == count && !is_sync_kiocb(iocb))
|
|
|
|
written = -EIOCBQUEUED;
|
|
|
|
return written;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_direct_write);
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
|
|
|
|
unsigned long nr_segs, loff_t pos, loff_t *ppos,
|
|
|
|
size_t count, ssize_t written)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
struct page *cached_page = NULL;
|
|
|
|
size_t bytes;
|
|
|
|
struct pagevec lru_pvec;
|
|
|
|
const struct iovec *cur_iov = iov; /* current iovec */
|
|
|
|
size_t iov_base = 0; /* offset in the current iovec */
|
|
|
|
char __user *buf;
|
|
|
|
|
|
|
|
pagevec_init(&lru_pvec, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* handle partial DIO write. Adjust cur_iov if needed.
|
|
|
|
*/
|
|
|
|
if (likely(nr_segs == 1))
|
|
|
|
buf = iov->iov_base + written;
|
|
|
|
else {
|
|
|
|
filemap_set_next_iovec(&cur_iov, &iov_base, written);
|
2005-05-01 23:58:35 +08:00
|
|
|
buf = cur_iov->iov_base + iov_base;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
unsigned long index;
|
|
|
|
unsigned long offset;
|
2005-06-07 04:35:54 +08:00
|
|
|
unsigned long maxlen;
|
2005-04-17 06:20:36 +08:00
|
|
|
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.
|
|
|
|
*/
|
2005-06-07 04:35:54 +08:00
|
|
|
maxlen = cur_iov->iov_len - iov_base;
|
|
|
|
if (maxlen > bytes)
|
|
|
|
maxlen = bytes;
|
|
|
|
fault_in_pages_readable(buf, maxlen);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);
|
|
|
|
if (!page) {
|
|
|
|
status = -ENOMEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = a_ops->prepare_write(file, page, offset, offset+bytes);
|
|
|
|
if (unlikely(status)) {
|
|
|
|
loff_t isize = i_size_read(inode);
|
2005-12-16 06:28:17 +08:00
|
|
|
|
|
|
|
if (status != AOP_TRUNCATED_PAGE)
|
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
if (status == AOP_TRUNCATED_PAGE)
|
|
|
|
continue;
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* prepare_write() may have instantiated a few blocks
|
|
|
|
* outside i_size. Trim these off again.
|
|
|
|
*/
|
|
|
|
if (pos + bytes > isize)
|
|
|
|
vmtruncate(inode, isize);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
status = a_ops->commit_write(file, page, offset, offset+bytes);
|
2005-12-16 06:28:17 +08:00
|
|
|
if (status == AOP_TRUNCATED_PAGE) {
|
|
|
|
page_cache_release(page);
|
|
|
|
continue;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
if (likely(copied > 0)) {
|
|
|
|
if (!status)
|
|
|
|
status = copied;
|
|
|
|
|
|
|
|
if (status >= 0) {
|
|
|
|
written += status;
|
|
|
|
count -= status;
|
|
|
|
pos += status;
|
|
|
|
buf += status;
|
2005-05-01 23:58:35 +08:00
|
|
|
if (unlikely(nr_segs > 1)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
filemap_set_next_iovec(&cur_iov,
|
|
|
|
&iov_base, status);
|
2005-06-26 05:55:42 +08:00
|
|
|
if (count)
|
|
|
|
buf = cur_iov->iov_base +
|
|
|
|
iov_base;
|
2005-06-07 04:35:54 +08:00
|
|
|
} else {
|
|
|
|
iov_base += status;
|
2005-05-01 23:58:35 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (unlikely(copied != bytes))
|
|
|
|
if (status >= 0)
|
|
|
|
status = -EFAULT;
|
|
|
|
unlock_page(page);
|
|
|
|
mark_page_accessed(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
if (status < 0)
|
|
|
|
break;
|
|
|
|
balance_dirty_pages_ratelimited(mapping);
|
|
|
|
cond_resched();
|
|
|
|
} while (count);
|
|
|
|
*ppos = pos;
|
|
|
|
|
|
|
|
if (cached_page)
|
|
|
|
page_cache_release(cached_page);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For now, when the user asks for O_SYNC, we'll actually give O_DSYNC
|
|
|
|
*/
|
|
|
|
if (likely(status >= 0)) {
|
|
|
|
if (unlikely((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
|
|
|
|
if (!a_ops->writepage || !is_sync_kiocb(iocb))
|
|
|
|
status = generic_osync_inode(inode, mapping,
|
|
|
|
OSYNC_METADATA|OSYNC_DATA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we get here for O_DIRECT writes then we must have fallen through
|
|
|
|
* to buffered writes (block instantiation inside i_size). So we sync
|
|
|
|
* the file data here, to try to honour O_DIRECT expectations.
|
|
|
|
*/
|
|
|
|
if (unlikely(file->f_flags & O_DIRECT) && written)
|
|
|
|
status = filemap_write_and_wait(mapping);
|
|
|
|
|
|
|
|
pagevec_lru_add(&lru_pvec);
|
|
|
|
return written ? written : status;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_buffered_write);
|
|
|
|
|
2005-09-10 15:26:28 +08:00
|
|
|
static ssize_t
|
2005-04-17 06:20:36 +08:00
|
|
|
__generic_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);
|
|
|
|
|
|
|
|
/* We can write back this queue in page reclaim */
|
|
|
|
current->backing_dev_info = mapping->backing_dev_info;
|
|
|
|
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;
|
|
|
|
|
2006-01-10 12:52:01 +08:00
|
|
|
file_update_time(file);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
|
|
|
|
if (unlikely(file->f_flags & O_DIRECT)) {
|
|
|
|
written = generic_file_direct_write(iocb, iov,
|
|
|
|
&nr_segs, pos, ppos, count, ocount);
|
|
|
|
if (written < 0 || written == count)
|
|
|
|
goto out;
|
|
|
|
/*
|
|
|
|
* direct-io write to a hole: fall through to buffered I/O
|
|
|
|
* for completing the rest of the request.
|
|
|
|
*/
|
|
|
|
pos += written;
|
|
|
|
count -= written;
|
|
|
|
}
|
|
|
|
|
|
|
|
written = generic_file_buffered_write(iocb, iov, nr_segs,
|
|
|
|
pos, ppos, count, written);
|
|
|
|
out:
|
|
|
|
current->backing_dev_info = NULL;
|
|
|
|
return written ? written : err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_aio_write_nolock);
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
generic_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;
|
|
|
|
struct inode *inode = mapping->host;
|
|
|
|
ssize_t ret;
|
|
|
|
loff_t pos = *ppos;
|
|
|
|
|
|
|
|
ret = __generic_file_aio_write_nolock(iocb, iov, nr_segs, ppos);
|
|
|
|
|
|
|
|
if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = sync_page_range_nolock(inode, mapping, pos, ret);
|
|
|
|
if (err < 0)
|
|
|
|
ret = err;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-09-10 15:26:28 +08:00
|
|
|
static ssize_t
|
2005-04-17 06:20:36 +08:00
|
|
|
__generic_file_write_nolock(struct file *file, const struct iovec *iov,
|
|
|
|
unsigned long nr_segs, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct kiocb kiocb;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
init_sync_kiocb(&kiocb, file);
|
|
|
|
ret = __generic_file_aio_write_nolock(&kiocb, iov, nr_segs, ppos);
|
|
|
|
if (ret == -EIOCBQUEUED)
|
|
|
|
ret = wait_on_sync_kiocb(&kiocb);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
generic_file_write_nolock(struct file *file, const struct iovec *iov,
|
|
|
|
unsigned long nr_segs, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct kiocb kiocb;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
init_sync_kiocb(&kiocb, file);
|
|
|
|
ret = generic_file_aio_write_nolock(&kiocb, iov, nr_segs, ppos);
|
|
|
|
if (-EIOCBQUEUED == ret)
|
|
|
|
ret = wait_on_sync_kiocb(&kiocb);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_write_nolock);
|
|
|
|
|
|
|
|
ssize_t generic_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);
|
|
|
|
|
2006-01-10 07:59:24 +08:00
|
|
|
mutex_lock(&inode->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = __generic_file_aio_write_nolock(iocb, &local_iov, 1,
|
|
|
|
&iocb->ki_pos);
|
2006-01-10 07:59:24 +08:00
|
|
|
mutex_unlock(&inode->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
|
|
|
|
ssize_t err;
|
|
|
|
|
|
|
|
err = sync_page_range(inode, mapping, pos, ret);
|
|
|
|
if (err < 0)
|
|
|
|
ret = err;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_aio_write);
|
|
|
|
|
|
|
|
ssize_t generic_file_write(struct file *file, const char __user *buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
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 };
|
|
|
|
|
2006-01-10 07:59:24 +08:00
|
|
|
mutex_lock(&inode->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = __generic_file_write_nolock(file, &local_iov, 1, ppos);
|
2006-01-10 07:59:24 +08:00
|
|
|
mutex_unlock(&inode->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
|
|
|
|
ssize_t err;
|
|
|
|
|
|
|
|
err = sync_page_range(inode, mapping, *ppos - ret, ret);
|
|
|
|
if (err < 0)
|
|
|
|
ret = err;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_write);
|
|
|
|
|
|
|
|
ssize_t generic_file_readv(struct file *filp, const struct iovec *iov,
|
|
|
|
unsigned long nr_segs, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct kiocb kiocb;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
init_sync_kiocb(&kiocb, filp);
|
|
|
|
ret = __generic_file_aio_read(&kiocb, iov, nr_segs, ppos);
|
|
|
|
if (-EIOCBQUEUED == ret)
|
|
|
|
ret = wait_on_sync_kiocb(&kiocb);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_readv);
|
|
|
|
|
|
|
|
ssize_t generic_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;
|
|
|
|
|
2006-01-10 07:59:24 +08:00
|
|
|
mutex_lock(&inode->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = __generic_file_write_nolock(file, iov, nr_segs, ppos);
|
2006-01-10 07:59:24 +08:00
|
|
|
mutex_unlock(&inode->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = sync_page_range(inode, mapping, *ppos - ret, ret);
|
|
|
|
if (err < 0)
|
|
|
|
ret = err;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_file_writev);
|
|
|
|
|
|
|
|
/*
|
2006-01-10 07:59:24 +08:00
|
|
|
* Called under i_mutex for writes to S_ISREG files. Returns -EIO if something
|
2005-04-17 06:20:36 +08:00
|
|
|
* went wrong during pagecache shootdown.
|
|
|
|
*/
|
2005-09-10 15:26:28 +08:00
|
|
|
static ssize_t
|
2005-04-17 06:20:36 +08:00
|
|
|
generic_file_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
|
|
|
|
loff_t offset, unsigned long nr_segs)
|
|
|
|
{
|
|
|
|
struct file *file = iocb->ki_filp;
|
|
|
|
struct address_space *mapping = file->f_mapping;
|
|
|
|
ssize_t retval;
|
|
|
|
size_t write_len = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it's a write, unmap all mmappings of the file up-front. This
|
|
|
|
* will cause any pte dirty bits to be propagated into the pageframes
|
|
|
|
* for the subsequent filemap_write_and_wait().
|
|
|
|
*/
|
|
|
|
if (rw == WRITE) {
|
|
|
|
write_len = iov_length(iov, nr_segs);
|
|
|
|
if (mapping_mapped(mapping))
|
|
|
|
unmap_mapping_range(mapping, offset, write_len, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
retval = filemap_write_and_wait(mapping);
|
|
|
|
if (retval == 0) {
|
|
|
|
retval = mapping->a_ops->direct_IO(rw, iocb, iov,
|
|
|
|
offset, nr_segs);
|
|
|
|
if (rw == WRITE && mapping->nrpages) {
|
|
|
|
pgoff_t end = (offset + write_len - 1)
|
|
|
|
>> PAGE_CACHE_SHIFT;
|
|
|
|
int err = invalidate_inode_pages2_range(mapping,
|
|
|
|
offset >> PAGE_CACHE_SHIFT, end);
|
|
|
|
if (err)
|
|
|
|
retval = err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|