diff -ur linux-2.4.6-pre2-virgin/include/linux/mm.h linux-2.4.6-pre2/include/linux/mm.h
--- linux-2.4.6-pre2-virgin/include/linux/mm.h Sun Jun 10 00:44:01 2001
+++ linux-2.4.6-pre2/include/linux/mm.h Sat Jun 9 23:19:54 2001
@@ -169,6 +169,7 @@
#define PG_inactive_clean 11
#define PG_highmem 12
#define PG_checked 13 /* kill me in 2.5.<early>. */
+#define PG_marker 14
/* bits 21-29 unused */
#define PG_arch_1 30
#define PG_reserved 31
@@ -242,6 +243,9 @@
#define PageInactiveClean(page) test_bit(PG_inactive_clean, &(page)->flags)
#define SetPageInactiveClean(page) set_bit(PG_inactive_clean, &(page)->flags)
#define ClearPageInactiveClean(page) clear_bit(PG_inactive_clean, &(page)->flags)
+
+#define PageMarker(page) test_bit(PG_marker, &(page)->flags)
+#define SetPageMarker(page) set_bit(PG_marker, &(page)->flags)
#ifdef CONFIG_HIGHMEM
#define PageHighMem(page) test_bit(PG_highmem, &(page)->flags)
diff -ur linux-2.4.6-pre2-virgin/include/linux/swap.h linux-2.4.6-pre2/include/linux/swap.h
--- linux-2.4.6-pre2-virgin/include/linux/swap.h Sun Jun 10 00:44:01 2001
+++ linux-2.4.6-pre2/include/linux/swap.h Sat Jun 9 23:19:54 2001
@@ -205,6 +205,16 @@
page->zone->inactive_dirty_pages++; \
}
+/* Like the above, but add us after the bookmark. */
+#define add_page_to_inactive_dirty_list_marker(page) { \
+ DEBUG_ADD_PAGE \
+ ZERO_PAGE_BUG \
+ SetPageInactiveDirty(page); \
+ list_add(&(page)->lru, marker_lru); \
+ nr_inactive_dirty_pages++; \
+ page->zone->inactive_dirty_pages++; \
+}
+
#define add_page_to_inactive_clean_list(page) { \
DEBUG_ADD_PAGE \
ZERO_PAGE_BUG \
diff -ur linux-2.4.6-pre2-virgin/mm/vmscan.c linux-2.4.6-pre2/mm/vmscan.c
--- linux-2.4.6-pre2-virgin/mm/vmscan.c Sun Jun 10 00:44:02 2001
+++ linux-2.4.6-pre2/mm/vmscan.c Sun Jun 10 00:57:25 2001
@@ -407,7 +407,7 @@
/**
* page_launder - clean dirty inactive pages, move to inactive_clean list
* @gfp_mask: what operations we are allowed to do
- * @sync: should we wait synchronously for the cleaning of pages
+ * @sync: are we allowed to do synchronous IO in emergencies ?
*
* When this function is called, we are most likely low on free +
* inactive_clean pages. Since we want to refill those pages as
@@ -428,20 +428,54 @@
#define CAN_DO_BUFFERS (gfp_mask & __GFP_BUFFER)
int page_launder(int gfp_mask, int sync)
{
+ static int cannot_free_pages;
int launder_loop, maxscan, cleaned_pages, maxlaunder;
- struct list_head * page_lru;
+ struct list_head * page_lru, * marker_lru;
struct page * page;
+ /* Our bookmark of where we are in the inactive_dirty list. */
+ struct page marker_page_struct = {
+ flags: (1<<PG_marker),
+ lru: { NULL, NULL },
+ };
+ marker_lru = &marker_page_struct.lru;
+
launder_loop = 0;
maxlaunder = 0;
cleaned_pages = 0;
dirty_page_rescan:
spin_lock(&pagemap_lru_lock);
- maxscan = nr_inactive_dirty_pages;
- while ((page_lru = inactive_dirty_list.prev) != &inactive_dirty_list &&
- maxscan-- > 0) {
+ /*
+ * By not scanning all inactive dirty pages we'll write out
+ * really old dirty pages before evicting newer clean pages.
+ * This should cause some LRU behaviour if we have a large
+ * amount of inactive pages (due to eg. drop behind).
+ *
+ * It also makes us accumulate dirty pages until we have enough
+ * to be worth writing to disk without causing excessive disk
+ * seeks and eliminates the infinite penalty clean pages incurred
+ * vs. dirty pages.
+ */
+ maxscan = nr_inactive_dirty_pages / 4;
+ if (launder_loop)
+ maxscan *= 2;
+ list_add_tail(marker_lru, &inactive_dirty_list);
+ while ((page_lru = marker_lru->prev) != &inactive_dirty_list &&
+ maxscan-- > 0 && free_shortage()) {
page = list_entry(page_lru, struct page, lru);
+ /* We move the bookmark forward by flipping the page ;) */
+ list_del(page_lru);
+ list_add(page_lru, marker_lru);
+
+ /* Don't waste CPU if chances are we cannot free anything. */
+ if (launder_loop && maxlaunder < 0 && cannot_free_pages)
+ break;
+
+ /* Skip other people's marker pages. */
+ if (PageMarker(page)) {
+ continue;
+ }
/* Wrong page on list?! (list corruption, should not happen) */
if (!PageInactiveDirty(page)) {
@@ -454,7 +488,6 @@
/* Page is or was in use? Move it to the active list. */
if (PageReferenced(page) || page->age > 0 ||
- page->zone->free_pages > page->zone->pages_high ||
(!page->buffers && page_count(page) > 1) ||
page_ramdisk(page)) {
del_page_from_inactive_dirty_list(page);
@@ -464,11 +497,9 @@
/*
* The page is locked. IO in progress?
- * Move it to the back of the list.
+ * Skip the page, we'll take a look when it unlocks.
*/
if (TryLockPage(page)) {
- list_del(page_lru);
- list_add(page_lru, &inactive_dirty_list);
continue;
}
@@ -482,10 +513,8 @@
if (!writepage)
goto page_active;
- /* First time through? Move it to the back of the list */
+ /* First time through? Skip the page. */
if (!launder_loop || !CAN_DO_IO) {
- list_del(page_lru);
- list_add(page_lru, &inactive_dirty_list);
UnlockPage(page);
continue;
}
@@ -544,7 +573,7 @@
/* The buffers were not freed. */
if (!clearedbuf) {
- add_page_to_inactive_dirty_list(page);
+ add_page_to_inactive_dirty_list_marker(page);
/* The page was only in the buffer cache. */
} else if (!page->mapping) {
@@ -600,6 +629,8 @@
UnlockPage(page);
}
}
+ /* Remove our marker. */
+ list_del(marker_lru);
spin_unlock(&pagemap_lru_lock);
/*
@@ -615,16 +646,29 @@
*/
if ((CAN_DO_IO || CAN_DO_BUFFERS) && !launder_loop && free_shortage()) {
launder_loop = 1;
- /* If we cleaned pages, never do synchronous IO. */
- if (cleaned_pages)
+ /*
+ * If we, or the previous process running page_launder(),
+ * managed to free any pages we never do synchronous IO.
+ */
+ if (cleaned_pages || !cannot_free_pages)
sync = 0;
+ /* Else, do synchronous IO (if we are allowed to). */
+ else if (sync)
+ sync = 1;
/* We only do a few "out of order" flushes. */
maxlaunder = MAX_LAUNDER;
- /* Kflushd takes care of the rest. */
+ /* Let bdflush take care of the rest. */
wakeup_bdflush(0);
goto dirty_page_rescan;
}
+ /*
+ * If we failed to free pages (because all pages are dirty)
+ * we remember this for the next time. This will prevent us
+ * from wasting too much CPU here.
+ */
+ cannot_free_pages = !cleaned_pages;
+
/* Return the number of pages moved to the inactive_clean list. */
return cleaned_pages;
}
@@ -852,7 +896,7 @@
* list, so this is a relatively cheap operation.
*/
if (free_shortage()) {
- ret += page_launder(gfp_mask, user);
+ ret += page_launder(gfp_mask, 1);
shrink_dcache_memory(DEF_PRIORITY, gfp_mask);
shrink_icache_memory(DEF_PRIORITY, gfp_mask);
}