--- vmscan.c.~1~ 2002-11-28 18:53:15.000000000 -0500 +++ vmscan.c 2003-08-28 19:09:48.000000000 -0400 @@ -23,6 +23,11 @@ #include #include #include +#include +#include +#ifdef CONFIG_QUOTA +#include +#endif #include @@ -334,12 +339,52 @@ return 0; } +/* + * Wait for pages being laundered and move them + * to tail of inactive list if they are still freeable. + */ +static void process_laundry(struct page **laundry, int nr_pages) +{ + int i; + struct page *page; + + for (i = nr_pages - 1; i >= 0; i--) { + page = laundry[i]; + spin_lock(&pagemap_lru_lock); + + if (!PageLRU(page) || PageActive(page)) + goto next_page; + + if (TryLockPage(page)) { + if (PageLaunder(page)) { + spin_unlock(&pagemap_lru_lock); + wait_on_page(page); + i++; + continue; + } + goto next_page; + } + + if (!PageDirty(page) && !PageError(page) && page->mapping && (page_count(page) - !!page->buffers) == 2) { + list_del(&page->lru); + list_add_tail(&page->lru, &inactive_list); + } + + UnlockPage(page); +next_page: + spin_unlock(&pagemap_lru_lock); + page_cache_release(page); + } +} + static int FASTCALL(shrink_cache(int nr_pages, zone_t * classzone, unsigned int gfp_mask, int priority)); static int shrink_cache(int nr_pages, zone_t * classzone, unsigned int gfp_mask, int priority) { struct list_head * entry; + int nr_laundry = 0; int max_scan = nr_inactive_pages / priority; int max_mapped = min((nr_pages << (10 - priority)), max_scan / 10); + struct page *laundry[SWAP_CLUSTER_MAX]; spin_lock(&pagemap_lru_lock); while (--max_scan >= 0 && (entry = inactive_list.prev) != &inactive_list) { @@ -408,8 +453,14 @@ page_cache_get(page); spin_unlock(&pagemap_lru_lock); - writepage(page); - page_cache_release(page); + if (!writepage(page)) { + laundry[nr_laundry++] = page; + if (nr_laundry == SWAP_CLUSTER_MAX) { + process_laundry(laundry, nr_laundry); + nr_laundry = 0; + } + } else + page_cache_release(page); spin_lock(&pagemap_lru_lock); continue; @@ -483,7 +534,7 @@ */ spin_unlock(&pagemap_lru_lock); swap_out(priority, gfp_mask, classzone); - return nr_pages; + goto out; } /* @@ -519,6 +570,9 @@ break; } spin_unlock(&pagemap_lru_lock); +out: + if (nr_laundry) + process_laundry(laundry, nr_laundry); return nr_pages; } @@ -572,14 +626,22 @@ refill_inactive(ratio); nr_pages = shrink_cache(nr_pages, classzone, gfp_mask, priority); - if (nr_pages <= 0) - return 0; - shrink_dcache_memory(priority, gfp_mask); - shrink_icache_memory(priority, gfp_mask); + /* + * Keep an eye on unused inode/dcache/quota entries. + */ + ratio = (inodes_stat.nr_unused * sizeof(struct inode)) >> PAGE_SHIFT; + ratio += (dentry_stat.nr_unused * sizeof(struct dentry)) >> PAGE_SHIFT; +#ifdef CONFIG_QUOTA + ratio += (dqstats.free_dquots * sizeof(struct dquot)) >> PAGE_SHIFT; +#endif + if (nr_pages > 0 || ratio > SWAP_CLUSTER_MAX) { + shrink_dcache_memory(priority, gfp_mask); + shrink_icache_memory(priority, gfp_mask); #ifdef CONFIG_QUOTA - shrink_dqcache_memory(DEF_PRIORITY, gfp_mask); + shrink_dqcache_memory(priority, gfp_mask); #endif + } return nr_pages; }