diff options
Diffstat (limited to 'mm/filemap.c')
-rw-r--r-- | mm/filemap.c | 145 |
1 files changed, 67 insertions, 78 deletions
diff --git a/mm/filemap.c b/mm/filemap.c index ffda2b7c1..227bcd5a9 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -9,26 +9,17 @@ * most "normal" filesystems (but you don't /have/ to use this: * the NFS filesystem used to do this differently, for example) */ -#include <linux/stat.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> +#include <linux/malloc.h> #include <linux/shm.h> -#include <linux/errno.h> #include <linux/mman.h> -#include <linux/string.h> -#include <linux/malloc.h> -#include <linux/fs.h> #include <linux/locks.h> #include <linux/pagemap.h> #include <linux/swap.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/blkdev.h> #include <linux/file.h> #include <linux/swapctl.h> -#include <asm/system.h> #include <asm/pgtable.h> #include <asm/uaccess.h> @@ -153,7 +144,7 @@ static inline int shrink_one_page(struct page *page, int gfp_mask) } while (tmp != bh); /* Refuse to swap out all buffer pages */ - if ((buffermem >> PAGE_SHIFT) * 100 < (buffer_mem.min_percent * num_physpages)) + if (buffer_under_min()) goto next; } @@ -167,14 +158,9 @@ static inline int shrink_one_page(struct page *page, int gfp_mask) case 1: /* is it a swap-cache or page-cache page? */ if (page->inode) { - if (test_and_clear_bit(PG_referenced, &page->flags)) { - touch_page(page); - break; - } - age_page(page); - if (page->age) + if (test_and_clear_bit(PG_referenced, &page->flags)) break; - if (page_cache_size * 100 < (page_cache.min_percent * num_physpages)) + if (pgcache_under_min()) break; if (PageSwapCache(page)) { delete_from_swap_cache(page); @@ -188,6 +174,9 @@ static inline int shrink_one_page(struct page *page, int gfp_mask) if (test_and_clear_bit(PG_referenced, &page->flags)) break; + if (buffer_under_min()) + break; + /* is it a buffer cache page? */ if (bh && try_to_free_buffer(bh, &bh, 6)) return 1; @@ -211,7 +200,7 @@ int shrink_mmap(int priority, int gfp_mask) struct page * page; int count_max, count_min; - count_max = (limit<<2) >> (priority>>1); + count_max = limit; count_min = (limit<<2) >> (priority); page = mem_map + clock; @@ -225,7 +214,15 @@ int shrink_mmap(int priority, int gfp_mask) if (shrink_one_page(page, gfp_mask)) return 1; count_max--; - if (page->inode || page->buffers) + /* + * If the page we looked at was recyclable but we didn't + * reclaim it (presumably due to PG_referenced), don't + * count it as scanned. This way, the more referenced + * page cache pages we encounter, the more rapidly we + * will age them. + */ + if (atomic_read(&page->count) != 1 || + (!page->inode && !page->buffers)) count_min--; page++; clock++; @@ -292,7 +289,7 @@ static inline void add_to_page_cache(struct page * page, struct page **hash) { atomic_inc(&page->count); - page->flags &= ~((1 << PG_uptodate) | (1 << PG_error)); + page->flags = (page->flags & ~((1 << PG_uptodate) | (1 << PG_error))) | (1 << PG_referenced); page->offset = offset; add_page_to_inode_queue(inode, page); __add_page_to_hash_queue(page, hash); @@ -313,7 +310,7 @@ static unsigned long try_to_read_ahead(struct file * file, offset &= PAGE_MASK; switch (page_cache) { case 0: - page_cache = get_user_page(offset); + page_cache = __get_free_page(GFP_USER); if (!page_cache) break; default: @@ -327,7 +324,6 @@ static unsigned long try_to_read_ahead(struct file * file, */ page = mem_map + MAP_NR(page_cache); add_to_page_cache(page, inode, offset, hash); - set_bit(PG_referenced, &page->flags); inode->i_op->readpage(file, page); page_cache = 0; } @@ -736,7 +732,7 @@ no_cached_page: * page.. */ if (!page_cache) { - page_cache = get_user_page(pos & PAGE_MASK); + page_cache = __get_free_page(GFP_USER); /* * That could have slept, so go around to the * very beginning.. @@ -1002,7 +998,7 @@ found_page: * extra page -- better to overlap the allocation with the I/O. */ if (no_share && !new_page) { - new_page = get_user_page(address); + new_page = __get_free_page(GFP_USER); if (!new_page) goto failure; } @@ -1039,7 +1035,7 @@ success: return new_page; no_cached_page: - new_page = get_user_page(address); + new_page = __get_free_page(GFP_USER); if (!new_page) goto no_page; @@ -1067,8 +1063,7 @@ no_cached_page: * Do a very limited read-ahead if appropriate */ if (PageLocked(page)) - new_page = try_to_read_ahead(file, offset + PAGE_SIZE, - get_user_page(address + PAGE_SIZE)); + new_page = try_to_read_ahead(file, offset + PAGE_SIZE, 0); goto found_page; page_locked_wait: @@ -1520,39 +1515,58 @@ generic_file_write(struct file *file, const char *buf, { struct dentry *dentry = file->f_dentry; 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; - unsigned long pgpos, offset; - unsigned long bytes, written; - unsigned long pos; - long status, sync, didread; + unsigned long written; + long status, sync; if (!inode->i_op || !inode->i_op->updatepage) return -EIO; sync = file->f_flags & O_SYNC; - pos = *ppos; written = 0; - status = 0; if (file->f_flags & O_APPEND) pos = inode->i_size; + /* + * Check whether we've reached the file size limit. + */ + status = -EFBIG; + if (pos >= limit) { + send_sig(SIGXFSZ, current, 0); + goto out; + } + + status = 0; + /* + * Check whether to truncate the write, + * and send the signal if we do. + */ + if (count > limit - pos) { + send_sig(SIGXFSZ, current, 0); + count = limit - pos; + } + while (count) { + unsigned long bytes, pgpos, offset; /* * Try to find the page in the cache. If it isn't there, * allocate a free page. */ offset = (pos & ~PAGE_MASK); pgpos = pos & PAGE_MASK; - - if ((bytes = PAGE_SIZE - offset) > count) + bytes = PAGE_SIZE - offset; + if (bytes > count) bytes = count; hash = page_hash(inode, pgpos); - if (!(page = __find_page(inode, pgpos, *hash))) { + page = __find_page(inode, pgpos, *hash); + if (!page) { if (!page_cache) { - page_cache = get_user_page(pgpos); + page_cache = __get_free_page(GFP_USER); if (page_cache) continue; status = -ENOMEM; @@ -1563,51 +1577,25 @@ generic_file_write(struct file *file, const char *buf, page_cache = 0; } - /* - * Note: setting of the PG_locked bit is handled - * below the i_op->xxx interface. - */ - didread = 0; -page_wait: + /* Get exclusive IO access to the page.. */ wait_on_page(page); - if (PageUptodate(page)) - goto do_update_page; + set_bit(PG_locked, &page->flags); /* - * The page is not up-to-date ... if we're writing less - * than a full page of data, we may have to read it first. - * But if the page is past the current end of file, we must - * clear it before updating. + * Do the real work.. If the writer ends up delaying the write, + * the writer needs to increment the page use counts until he + * is done with the page. */ - if (bytes < PAGE_SIZE) { - if (pgpos < inode->i_size) { - status = -EIO; - if (didread >= 2) - goto done_with_page; - status = inode->i_op->readpage(file, page); - if (status < 0) - goto done_with_page; - didread++; - goto page_wait; - } else { - /* Must clear for partial writes */ - memset((void *) page_address(page), 0, - PAGE_SIZE); - } - } - /* - * N.B. We should defer setting PG_uptodate at least until - * the data is copied. A failure in i_op->updatepage() could - * leave the page with garbage data. - */ - set_bit(PG_uptodate, &page->flags); - -do_update_page: - /* All right, the page is there. Now update it. */ - status = inode->i_op->updatepage(file, page, buf, - offset, bytes, sync); -done_with_page: + bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes); + status = -EFAULT; + if (bytes) + status = inode->i_op->updatepage(file, page, offset, bytes, sync); + + /* Mark it unlocked again and drop the page.. */ + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); __free_page(page); + if (status < 0) break; @@ -1622,6 +1610,7 @@ done_with_page: if (page_cache) free_page(page_cache); +out: return written ? written : status; } |