summaryrefslogtreecommitdiffstats
path: root/mm/filemap.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-01-27 01:05:20 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-01-27 01:05:20 +0000
commit546db14ee74118296f425f3b91634fb767d67290 (patch)
tree22b613a3da8d4bf663eec5e155af01b87fdf9094 /mm/filemap.c
parent1e25e41c4f5474e14452094492dbc169b800e4c8 (diff)
Merge with Linux 2.3.23. The new bootmem stuff has broken various
platforms. At this time I've only verified that IP22 support compiles and IP27 actually works.
Diffstat (limited to 'mm/filemap.c')
-rw-r--r--mm/filemap.c235
1 files changed, 116 insertions, 119 deletions
diff --git a/mm/filemap.c b/mm/filemap.c
index 51624abcf..7451f46d9 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -21,6 +21,7 @@
#include <linux/swapctl.h>
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/highmem.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
@@ -75,24 +76,6 @@ static void remove_page_from_hash_queue(struct page * page)
atomic_dec(&page_cache_size);
}
-static void remove_page_from_inode_queue(struct page * page)
-{
- struct inode * inode = page->inode;
- struct page *prev, *next;
-
- inode->i_nrpages--;
- next = page->next;
- prev = page->prev;
- if (inode->i_pages == page)
- inode->i_pages = next;
- if (next)
- next->prev = prev;
- if (prev)
- prev->next = next;
- page->next = NULL;
- page->prev = NULL;
-}
-
/*
* 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
@@ -112,49 +95,53 @@ void remove_inode_page(struct page *page)
void invalidate_inode_pages(struct inode * inode)
{
- struct page ** p;
+ struct list_head *head, *curr;
struct page * page;
+ head = &inode->i_pages;
repeat:
spin_lock(&pagecache_lock);
- p = &inode->i_pages;
- while ((page = *p) != NULL) {
- get_page(page);
- if (TryLockPage(page)) {
- spin_unlock(&pagecache_lock);
- wait_on_page(page);
- page_cache_release(page);
- goto repeat;
- }
- if (page_count(page) != 2)
- printk("hm, busy page invalidated? (not necessarily a bug)\n");
+ curr = head->next;
+
+ while (curr != head) {
+ page = list_entry(curr, struct page, list);
+ curr = curr->next;
+
+ /* We cannot invalidate a locked page */
+ if (PageLocked(page))
+ continue;
+
lru_cache_del(page);
remove_page_from_inode_queue(page);
remove_page_from_hash_queue(page);
page->inode = NULL;
- UnlockPage(page);
- page_cache_release(page);
page_cache_release(page);
-
}
spin_unlock(&pagecache_lock);
}
+
/*
* Truncate the page cache at a set offset, removing the pages
* that are beyond that offset (and zeroing out partial pages).
*/
void truncate_inode_pages(struct inode * inode, unsigned long start)
{
- struct page ** p;
+ struct list_head *head, *curr;
+ unsigned long offset;
struct page * page;
int partial = 0;
repeat:
+ head = &inode->i_pages;
spin_lock(&pagecache_lock);
- p = &inode->i_pages;
- while ((page = *p) != NULL) {
- unsigned long offset = page->offset;
+ curr = head->next;
+ while (curr != head) {
+
+ page = list_entry(curr, struct page, list);
+ curr = curr->next;
+
+ offset = page->offset;
/* page wholly truncated - free it */
if (offset >= start) {
@@ -190,7 +177,6 @@ repeat:
*/
goto repeat;
}
- p = &page->next;
/*
* there is only one partial page possible.
*/
@@ -200,17 +186,14 @@ repeat:
offset = start - offset;
/* partial truncate, clear end of page */
if (offset < PAGE_CACHE_SIZE) {
- unsigned long address;
get_page(page);
spin_unlock(&pagecache_lock);
lock_page(page);
partial = 1;
- address = page_address(page);
- memset((void *) (offset + address), 0, PAGE_CACHE_SIZE - offset);
- flush_page_to_ram(address);
-
+ memclear_highpage_flush(page, offset,
+ PAGE_CACHE_SIZE-offset);
if (inode->i_op->flushpage)
inode->i_op->flushpage(inode, page, offset);
/*
@@ -255,7 +238,7 @@ int shrink_mmap(int priority, int gfp_mask)
/* don't account passes over not DMA pages */
if ((gfp_mask & __GFP_DMA) && !PageDMA(page))
goto dispose_continue;
- if (!(gfp_mask & __GFP_BIGMEM) && PageBIGMEM(page))
+ if (!(gfp_mask & __GFP_HIGHMEM) && PageHighMem(page))
goto dispose_continue;
count--;
@@ -291,7 +274,7 @@ int shrink_mmap(int priority, int gfp_mask)
goto unlock_continue;
/* page was locked, inode can't go away under us */
if (!page->inode) {
- atomic_sub(PAGE_CACHE_SIZE, &buffermem);
+ atomic_dec(&buffermem_pages);
goto made_buffer_progress;
}
spin_lock(&pagecache_lock);
@@ -431,16 +414,18 @@ static int waitfor_one_page(struct page *page)
static int do_buffer_fdatasync(struct inode *inode, unsigned long start, unsigned long end, int (*fn)(struct page *))
{
- struct page *next;
+ struct list_head *head, *curr;
+ struct page *page;
int retval = 0;
+ head = &inode->i_pages;
start &= PAGE_MASK;
spin_lock(&pagecache_lock);
- next = inode->i_pages;
- while (next) {
- struct page *page = next;
- next = page->next;
+ curr = head->next;
+ while (curr != head) {
+ page = list_entry(curr, struct page, list);
+ curr = curr->next;
if (!page->buffers)
continue;
if (page->offset >= end)
@@ -458,7 +443,7 @@ static int do_buffer_fdatasync(struct inode *inode, unsigned long start, unsigne
UnlockPage(page);
spin_lock(&pagecache_lock);
- next = page->next;
+ curr = page->list.next;
page_cache_release(page);
}
spin_unlock(&pagecache_lock);
@@ -487,6 +472,7 @@ static inline void __add_to_page_cache(struct page * page,
struct inode * inode, unsigned long offset,
struct page **hash)
{
+ struct page *alias;
unsigned long flags;
flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced));
@@ -497,6 +483,9 @@ static inline void __add_to_page_cache(struct page * page,
add_page_to_inode_queue(inode, page);
__add_page_to_hash_queue(page, hash);
lru_cache_add(page);
+ alias = __find_page_nolock(inode, offset, *hash);
+ if (alias != page)
+ BUG();
}
void add_to_page_cache(struct page * page, struct inode * inode, unsigned long offset)
@@ -532,10 +521,9 @@ int add_to_page_cache_unique(struct page * page,
*/
static inline void page_cache_read(struct file * file, unsigned long offset)
{
- unsigned long new_page;
struct inode *inode = file->f_dentry->d_inode;
- struct page ** hash = page_hash(inode, offset);
- struct page * page;
+ struct page **hash = page_hash(inode, offset);
+ struct page *page;
spin_lock(&pagecache_lock);
page = __find_page_nolock(inode, offset, *hash);
@@ -543,22 +531,20 @@ static inline void page_cache_read(struct file * file, unsigned long offset)
if (page)
return;
- new_page = page_cache_alloc();
- if (!new_page)
+ page = page_cache_alloc();
+ if (!page)
return;
- page = page_cache_entry(new_page);
if (!add_to_page_cache_unique(page, inode, offset, hash)) {
inode->i_op->readpage(file, page);
page_cache_release(page);
return;
}
-
/*
* We arrive here in the unlikely event that someone
* raced with us and added our page to the cache first.
*/
- page_cache_free(new_page);
+ page_cache_free(page);
return;
}
@@ -962,13 +948,13 @@ void do_generic_file_read(struct file * filp, loff_t *ppos, read_descriptor_t *
{
struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode;
- size_t pos, pgpos, page_cache;
+ size_t pos, pgpos;
+ struct page *cached_page;
int reada_ok;
int error;
int max_readahead = get_max_readahead(inode);
- page_cache = 0;
-
+ cached_page = NULL;
pos = *ppos;
pgpos = pos & PAGE_CACHE_MASK;
/*
@@ -1051,7 +1037,7 @@ page_ok:
* "pos" here (the actor routine has to update the user buffer
* pointers and the remaining count).
*/
- nr = actor(desc, (const char *) (page_address(page) + offset), nr);
+ nr = actor(desc, page, offset, nr);
pos += nr;
page_cache_release(page);
if (nr && desc->count)
@@ -1105,10 +1091,10 @@ no_cached_page:
*
* We get here with the page cache lock held.
*/
- if (!page_cache) {
+ if (!cached_page) {
spin_unlock(&pagecache_lock);
- page_cache = page_cache_alloc();
- if (!page_cache) {
+ cached_page = page_cache_alloc();
+ if (!cached_page) {
desc->error = -ENOMEM;
break;
}
@@ -1126,29 +1112,35 @@ no_cached_page:
/*
* Ok, add the new page to the hash-queues...
*/
- page = page_cache_entry(page_cache);
+ page = cached_page;
__add_to_page_cache(page, inode, pos & PAGE_CACHE_MASK, hash);
spin_unlock(&pagecache_lock);
+ cached_page = NULL;
- page_cache = 0;
goto readpage;
}
*ppos = pos;
filp->f_reada = 1;
- if (page_cache)
- page_cache_free(page_cache);
+ if (cached_page)
+ page_cache_free(cached_page);
UPDATE_ATIME(inode);
}
-static int file_read_actor(read_descriptor_t * desc, const char *area, unsigned long size)
+static int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size)
{
- unsigned long left;
- unsigned long count = desc->count;
+ unsigned long kaddr;
+ unsigned long left, count = desc->count;
if (size > count)
size = count;
- left = __copy_to_user(desc->buf, area, size);
+ /*
+ * FIXME: We cannot yet sleep with kmaps held.
+ */
+ kaddr = kmap(page, KM_READ);
+ left = __copy_to_user(desc->buf, (void *)(kaddr+offset), size);
+ kunmap(kaddr, KM_READ);
+
if (left) {
size -= left;
desc->error = -EFAULT;
@@ -1187,8 +1179,9 @@ ssize_t generic_file_read(struct file * filp, char * buf, size_t count, loff_t *
return retval;
}
-static int file_send_actor(read_descriptor_t * desc, const char *area, unsigned long size)
+static int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset , unsigned long size)
{
+ unsigned long kaddr;
ssize_t written;
unsigned long count = desc->count;
struct file *file = (struct file *) desc->buf;
@@ -1198,7 +1191,9 @@ static int file_send_actor(read_descriptor_t * desc, const char *area, unsigned
size = count;
old_fs = get_fs();
set_fs(KERNEL_DS);
- written = file->f_op->write(file, area, size, &file->f_pos);
+ kaddr = kmap(page, KM_READ);
+ written = file->f_op->write(file, (char *)kaddr + offset, size, &file->f_pos);
+ kunmap(kaddr, KM_READ);
set_fs(old_fs);
if (written < 0) {
desc->error = written;
@@ -1298,14 +1293,13 @@ out:
* XXX - at some point, this should return unique values to indicate to
* the caller whether this is EIO, OOM, or SIGBUS.
*/
-static unsigned long filemap_nopage(struct vm_area_struct * area,
+static struct page * filemap_nopage(struct vm_area_struct * area,
unsigned long address, int no_share)
{
- struct file * file = area->vm_file;
- struct dentry * dentry = file->f_dentry;
- struct inode * inode = dentry->d_inode;
- struct page * page, **hash;
- unsigned long old_page;
+ struct file *file = area->vm_file;
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct page *page, **hash, *old_page;
unsigned long offset = address - area->vm_start + area->vm_offset;
@@ -1317,7 +1311,7 @@ static unsigned long filemap_nopage(struct vm_area_struct * area,
*/
if ((offset >= inode->i_size) &&
(area->vm_flags & VM_SHARED) && (area->vm_mm == current->mm))
- return 0;
+ return NULL;
/*
* Do we have something in the page cache already?
@@ -1340,12 +1334,14 @@ success:
* Found the page and have a reference on it, need to check sharing
* and possibly copy it over to another page..
*/
- old_page = page_address(page);
+ old_page = page;
if (no_share) {
- unsigned long new_page = page_cache_alloc();
+ struct page *new_page = page_cache_alloc();
if (new_page) {
- copy_page(new_page, old_page);
+ if (PageHighMem(new_page) || PageHighMem(old_page))
+ BUG();
+ copy_highpage(new_page, old_page);
flush_page_to_ram(new_page);
}
page_cache_release(page);
@@ -1411,7 +1407,7 @@ page_not_uptodate:
* mm layer so, possibly freeing the page cache page first.
*/
page_cache_release(page);
- return 0;
+ return NULL;
}
/*
@@ -1419,12 +1415,11 @@ page_not_uptodate:
* if the disk is full.
*/
static inline int do_write_page(struct inode * inode, struct file * file,
- const char * page_addr, unsigned long offset)
+ struct page * page, unsigned long offset)
{
int retval;
unsigned long size;
int (*writepage) (struct file *, struct page *);
- struct page * page;
size = offset + PAGE_SIZE;
/* refuse to extend file size.. */
@@ -1438,7 +1433,6 @@ static inline int do_write_page(struct inode * inode, struct file * file,
size -= offset;
retval = -EIO;
writepage = inode->i_op->writepage;
- page = mem_map + MAP_NR(page_addr);
lock_page(page);
retval = writepage(file, page);
@@ -1449,7 +1443,7 @@ static inline int do_write_page(struct inode * inode, struct file * file,
static int filemap_write_page(struct vm_area_struct * vma,
unsigned long offset,
- unsigned long page,
+ struct page * page,
int wait)
{
int result;
@@ -1466,7 +1460,7 @@ static int filemap_write_page(struct vm_area_struct * vma,
* and file could be released ... increment the count to be safe.
*/
get_file(file);
- result = do_write_page(inode, file, (const char *) page, offset);
+ result = do_write_page(inode, file, page, offset);
fput(file);
return result;
}
@@ -1480,7 +1474,7 @@ static int filemap_write_page(struct vm_area_struct * vma,
extern void wakeup_bdflush(int);
int filemap_swapout(struct vm_area_struct * vma, struct page * page)
{
- int retval = filemap_write_page(vma, page->offset, page_address(page), 0);
+ int retval = filemap_write_page(vma, page->offset, page, 0);
wakeup_bdflush(0);
return retval;
}
@@ -1489,7 +1483,6 @@ static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma,
unsigned long address, unsigned int flags)
{
pte_t pte = *ptep;
- unsigned long pageaddr;
struct page *page;
int error;
@@ -1502,8 +1495,7 @@ static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma,
flush_cache_page(vma, address);
set_pte(ptep, pte_mkclean(pte));
flush_tlb_page(vma, address);
- pageaddr = pte_page(pte);
- page = page_cache_entry(pageaddr);
+ page = pte_page(pte);
get_page(page);
} else {
if (pte_none(pte))
@@ -1512,17 +1504,19 @@ static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma,
pte_clear(ptep);
flush_tlb_page(vma, address);
if (!pte_present(pte)) {
- swap_free(pte_val(pte));
+ swap_free(pte);
return 0;
}
- pageaddr = pte_page(pte);
+ page = pte_page(pte);
if (!pte_dirty(pte) || flags == MS_INVALIDATE) {
- page_cache_free(pageaddr);
+ page_cache_free(page);
return 0;
}
}
- error = filemap_write_page(vma, address - vma->vm_start + vma->vm_offset, pageaddr, 1);
- page_cache_free(pageaddr);
+ if (PageHighMem(page))
+ BUG();
+ error = filemap_write_page(vma, address - vma->vm_start + vma->vm_offset, page, 1);
+ page_cache_free(page);
return error;
}
@@ -1537,7 +1531,7 @@ static inline int filemap_sync_pte_range(pmd_t * pmd,
if (pmd_none(*pmd))
return 0;
if (pmd_bad(*pmd)) {
- printk("filemap_sync_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd));
+ pmd_ERROR(*pmd);
pmd_clear(pmd);
return 0;
}
@@ -1552,7 +1546,7 @@ static inline int filemap_sync_pte_range(pmd_t * pmd,
error |= filemap_sync_pte(pte, vma, address + offset, flags);
address += PAGE_SIZE;
pte++;
- } while (address < end);
+ } while (address && (address < end));
return error;
}
@@ -1567,7 +1561,7 @@ static inline int filemap_sync_pmd_range(pgd_t * pgd,
if (pgd_none(*pgd))
return 0;
if (pgd_bad(*pgd)) {
- printk("filemap_sync_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd));
+ pgd_ERROR(*pgd);
pgd_clear(pgd);
return 0;
}
@@ -1582,7 +1576,7 @@ static inline int filemap_sync_pmd_range(pgd_t * pgd,
error |= filemap_sync_pte_range(pmd, address, end - address, vma, offset, flags);
address = (address + PMD_SIZE) & PMD_MASK;
pmd++;
- } while (address < end);
+ } while (address && (address < end));
return error;
}
@@ -1595,11 +1589,13 @@ static int filemap_sync(struct vm_area_struct * vma, unsigned long address,
dir = pgd_offset(vma->vm_mm, address);
flush_cache_range(vma->vm_mm, end - size, end);
- while (address < end) {
+ if (address >= end)
+ BUG();
+ do {
error |= filemap_sync_pmd_range(dir, address, end - address, vma, flags);
address = (address + PGDIR_SIZE) & PGDIR_MASK;
dir++;
- }
+ } while (address && (address < end));
flush_tlb_range(vma->vm_mm, end - size, end);
return error;
}
@@ -1775,12 +1771,13 @@ generic_file_write(struct file *file, const char *buf,
struct inode *inode = dentry->d_inode;
unsigned long pos = *ppos;
unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
- struct page *page, **hash;
- unsigned long page_cache = 0;
+ struct page *page, **hash, *cached_page;
unsigned long written;
long status;
int err;
+ cached_page = NULL;
+
down(&inode->i_sem);
err = file->f_error;
if (err) {
@@ -1828,18 +1825,18 @@ generic_file_write(struct file *file, const char *buf,
repeat_find:
page = __find_lock_page(inode, pgpos, hash);
if (!page) {
- if (!page_cache) {
- page_cache = page_cache_alloc();
- if (page_cache)
+ if (!cached_page) {
+ cached_page = page_cache_alloc();
+ if (cached_page)
goto repeat_find;
status = -ENOMEM;
break;
}
- page = page_cache_entry(page_cache);
+ page = cached_page;
if (add_to_page_cache_unique(page,inode,pgpos,hash))
goto repeat_find;
- page_cache = 0;
+ cached_page = NULL;
}
/* We have exclusive IO access to the page.. */
@@ -1870,8 +1867,8 @@ repeat_find:
}
*ppos = pos;
- if (page_cache)
- page_cache_free(page_cache);
+ if (cached_page)
+ page_cache_free(cached_page);
err = written ? written : status;
out:
@@ -1897,11 +1894,11 @@ void put_cached_page(unsigned long addr)
page_cache_release(page);
}
-void __init page_cache_init(unsigned long memory_size)
+void __init page_cache_init(unsigned long mempages)
{
unsigned long htable_size, order;
- htable_size = memory_size >> PAGE_SHIFT;
+ htable_size = mempages;
htable_size *= sizeof(struct page *);
for(order = 0; (PAGE_SIZE << order) < htable_size; order++)
;
@@ -1921,5 +1918,5 @@ void __init page_cache_init(unsigned long memory_size)
(1 << page_hash_bits), order, (PAGE_SIZE << order));
if (!page_hash_table)
panic("Failed to allocate page hash table\n");
- memset(page_hash_table, 0, PAGE_HASH_SIZE * sizeof(struct page *));
+ memset((void *)page_hash_table, 0, PAGE_HASH_SIZE * sizeof(struct page *));
}