summaryrefslogtreecommitdiffstats
path: root/mm/page_alloc.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
commit27cfca1ec98e91261b1a5355d10a8996464b63af (patch)
tree8e895a53e372fa682b4c0a585b9377d67ed70d0e /mm/page_alloc.c
parent6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff)
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too o Upgrade to 2.1.89. Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'mm/page_alloc.c')
-rw-r--r--mm/page_alloc.c128
1 files changed, 95 insertions, 33 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 07264f81e..ed748bbfb 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -19,6 +19,7 @@
#include <linux/swapctl.h>
#include <linux/interrupt.h>
#include <linux/init.h>
+#include <linux/pagemap.h>
#include <asm/dma.h>
#include <asm/system.h> /* for cli()/sti() */
@@ -101,6 +102,46 @@ static inline void remove_mem_queue(struct page * entry)
static spinlock_t page_alloc_lock;
#endif
+/*
+ * This routine is used by the kernel swap deamon to determine
+ * whether we have "enough" free pages. It is fairly arbitrary,
+ * but this had better return false if any reasonable "get_free_page()"
+ * allocation could currently fail..
+ *
+ * Currently we approve of the following situations:
+ * - the highest memory order has two entries
+ * - the highest memory order has one free entry and:
+ * - the next-highest memory order has two free entries
+ * - the highest memory order has one free entry and:
+ * - the next-highest memory order has one free entry
+ * - the next-next-highest memory order has two free entries
+ *
+ * [previously, there had to be two entries of the highest memory
+ * order, but this lead to problems on large-memory machines.]
+ */
+int free_memory_available(void)
+{
+ int i, retval = 0;
+ unsigned long flags;
+ struct free_area_struct * list = NULL;
+
+ spin_lock_irqsave(&page_alloc_lock, flags);
+ /* We fall through the loop if the list contains one
+ * item. -- thanks to Colin Plumb <colin@nyx.net>
+ */
+ for (i = 1; i < 4; ++i) {
+ list = free_area + NR_MEM_LISTS - i;
+ if (list->next == memory_head(list))
+ break;
+ if (list->next->next == memory_head(list))
+ continue;
+ retval = 1;
+ break;
+ }
+ spin_unlock_irqrestore(&page_alloc_lock, flags);
+ return retval;
+}
+
static inline void free_pages_ok(unsigned long map_nr, unsigned long order)
{
struct free_area_struct *area = free_area + order;
@@ -133,9 +174,12 @@ static inline void free_pages_ok(unsigned long map_nr, unsigned long order)
void __free_page(struct page *page)
{
if (!PageReserved(page) && atomic_dec_and_test(&page->count)) {
- delete_from_swap_cache(page);
+ if (PageSwapCache(page))
+ panic ("Freeing swap cache page");
free_pages_ok(page->map_nr, 0);
}
+ if (PageSwapCache(page) && atomic_read(&page->count) == 1)
+ panic ("Releasing swap cache page");
}
void free_pages(unsigned long addr, unsigned long order)
@@ -147,10 +191,14 @@ void free_pages(unsigned long addr, unsigned long order)
if (PageReserved(map))
return;
if (atomic_dec_and_test(&map->count)) {
- delete_from_swap_cache(map);
+ if (PageSwapCache(map))
+ panic ("Freeing swap cache pages");
free_pages_ok(map_nr, order);
return;
}
+ if (PageSwapCache(map) && atomic_read(&map->count) == 1)
+ panic ("Releasing swap cache pages at %p",
+ __builtin_return_address(0));
}
}
@@ -161,11 +209,13 @@ void free_pages(unsigned long addr, unsigned long order)
change_bit((index) >> (1+(order)), (area)->map)
#define CAN_DMA(x) (PageDMA(x))
#define ADDRESS(x) (PAGE_OFFSET + ((x) << PAGE_SHIFT))
-#define RMQUEUE(order, dma) \
+#define RMQUEUE(order, maxorder, dma) \
do { struct free_area_struct * area = free_area+order; \
unsigned long new_order = order; \
- do { struct page *prev = memory_head(area), *ret; \
- while (memory_head(area) != (ret = prev->next)) { \
+ do { struct page *prev = memory_head(area), *ret = prev->next; \
+ while (memory_head(area) != ret) { \
+ if (new_order >= maxorder && ret->next == prev) \
+ break; \
if (!dma || CAN_DMA(ret)) { \
unsigned long map_nr = ret->map_nr; \
(prev->next = ret->next)->prev = prev; \
@@ -176,6 +226,7 @@ do { struct free_area_struct * area = free_area+order; \
return ADDRESS(map_nr); \
} \
prev = ret; \
+ ret = ret->next; \
} \
new_order++; area++; \
} while (new_order < NR_MEM_LISTS); \
@@ -194,36 +245,40 @@ do { unsigned long size = 1 << high; \
map->age = PAGE_INITIAL_AGE; \
} while (0)
-unsigned long __get_free_pages(int priority, unsigned long order, int dma)
+unsigned long __get_free_pages(int gfp_mask, unsigned long order)
{
- unsigned long flags;
- int reserved_pages;
+ unsigned long flags, maxorder;
if (order >= NR_MEM_LISTS)
- return 0;
+ goto nopage;
- if (in_interrupt() && priority != GFP_ATOMIC) {
+ /*
+ * "maxorder" is the highest order number that we're allowed
+ * to empty in order to find a free page..
+ */
+ maxorder = order + NR_MEM_LISTS/3;
+ if (gfp_mask & __GFP_MED)
+ maxorder += NR_MEM_LISTS/3;
+ if ((gfp_mask & __GFP_HIGH) || maxorder > NR_MEM_LISTS)
+ maxorder = NR_MEM_LISTS;
+
+ if (in_interrupt() && (gfp_mask & __GFP_WAIT)) {
static int count = 0;
if (++count < 5) {
printk("gfp called nonatomically from interrupt %p\n",
- return_address());
- priority = GFP_ATOMIC;
+ return_address());
+ gfp_mask &= ~__GFP_WAIT;
}
}
- reserved_pages = 5;
- if (priority != GFP_NFS)
- reserved_pages = min_free_pages;
repeat:
spin_lock_irqsave(&page_alloc_lock, flags);
- if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) {
- RMQUEUE(order, dma);
- spin_unlock_irqrestore(&page_alloc_lock, flags);
- return 0;
- }
+ RMQUEUE(order, maxorder, (gfp_mask & GFP_DMA));
spin_unlock_irqrestore(&page_alloc_lock, flags);
- if (priority != GFP_BUFFER && try_to_free_page(priority, dma, 1))
+ if ((gfp_mask & __GFP_WAIT) && try_to_free_page(gfp_mask))
goto repeat;
+
+nopage:
return 0;
}
@@ -315,31 +370,38 @@ __initfunc(unsigned long free_area_init(unsigned long start_mem, unsigned long e
void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
pte_t * page_table, unsigned long entry, int write_access)
{
- unsigned long page = __get_free_page(GFP_KERNEL);
+ unsigned long page;
+ struct page *page_map;
+
+ page_map = read_swap_cache(entry);
if (pte_val(*page_table) != entry) {
- free_page(page);
+ if (page_map)
+ free_page_and_swap_cache(page_address(page_map));
return;
}
- if (!page) {
+ if (!page_map) {
set_pte(page_table, BAD_PAGE);
swap_free(entry);
oom(tsk);
return;
}
- read_swap_page(entry, (char *) page);
- if (pte_val(*page_table) != entry) {
- free_page(page);
- return;
- }
+
+ page = page_address(page_map);
vma->vm_mm->rss++;
- tsk->maj_flt++;
- if (!write_access && add_to_swap_cache(&mem_map[MAP_NR(page)], entry)) {
- /* keep swap page allocated for the moment (swap cache) */
+ tsk->min_flt++;
+ swap_free(entry);
+
+ if (!write_access || is_page_shared(page_map)) {
set_pte(page_table, mk_pte(page, vma->vm_page_prot));
return;
}
+
+ /* The page is unshared, and we want write access. In this
+ case, it is safe to tear down the swap cache and give the
+ page over entirely to this process. */
+
+ delete_from_swap_cache(page_map);
set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
- swap_free(entry);
return;
}