From: William Lee Irwin III This patch implements wake-one semantics for page wakeups in a single step. Discrimination between distinct pages is achieved by passing the page to the wakeup function, which compares it to a pointer in its own on-stack structure containing the waitqueue element and the page. Bit discrimination is achieved by storing the bit number in that same structure and testing the bit in the wakeup function. Wake-one semantics are achieved by using WQ_FLAG_EXCLUSIVE in the codepaths waiting to acquire the bit for mutual exclusion. --- 25-akpm/mm/filemap.c | 64 ++++++++++++++++++++++++++++++++++++++++----------- 1 files changed, 51 insertions(+), 13 deletions(-) diff -puN mm/filemap.c~filtered_page mm/filemap.c --- 25/mm/filemap.c~filtered_page 2004-05-06 16:44:08.562340872 -0700 +++ 25-akpm/mm/filemap.c 2004-05-06 16:44:08.566340264 -0700 @@ -293,6 +293,40 @@ int add_to_page_cache_lru(struct page *p * at a cost of "thundering herd" phenomena during rare hash * collisions. */ +struct page_wait_queue { + struct page *page; + int bit; + wait_queue_t wait; +}; + +static int page_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + struct page *page = key; + struct page_wait_queue *wq; + + wq = container_of(wait, struct page_wait_queue, wait); + if (wq->page != page || test_bit(wq->bit, &page->flags)) + return 0; + else + return autoremove_wake_function(wait, mode, sync, NULL); +} + +#define __DEFINE_PAGE_WAIT(name, p, b, f) \ + struct page_wait_queue name = { \ + .page = p, \ + .bit = b, \ + .wait = { \ + .task = current, \ + .func = page_wake_function, \ + .flags = f, \ + .task_list = LIST_HEAD_INIT(name.wait.task_list),\ + }, \ + } + +#define DEFINE_PAGE_WAIT(name, p, b) __DEFINE_PAGE_WAIT(name, p, b, 0) +#define DEFINE_PAGE_WAIT_EXCLUSIVE(name, p, b) \ + __DEFINE_PAGE_WAIT(name, p, b, WQ_FLAG_EXCLUSIVE) + static wait_queue_head_t *page_waitqueue(struct page *page) { const struct zone *zone = page_zone(page); @@ -300,19 +334,28 @@ static wait_queue_head_t *page_waitqueue return &zone->wait_table[hash_ptr(page, zone->wait_table_bits)]; } +static void wake_up_page(struct page *page) +{ + const unsigned int mode = TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE; + wait_queue_head_t *waitqueue = page_waitqueue(page); + + if (waitqueue_active(waitqueue)) + __wake_up(waitqueue, mode, 1, page); +} + void fastcall wait_on_page_bit(struct page *page, int bit_nr) { wait_queue_head_t *waitqueue = page_waitqueue(page); - DEFINE_WAIT(wait); + DEFINE_PAGE_WAIT(wait, page, bit_nr); do { - prepare_to_wait(waitqueue, &wait, TASK_UNINTERRUPTIBLE); + prepare_to_wait(waitqueue, &wait.wait, TASK_UNINTERRUPTIBLE); if (test_bit(bit_nr, &page->flags)) { sync_page(page); io_schedule(); } } while (test_bit(bit_nr, &page->flags)); - finish_wait(waitqueue, &wait); + finish_wait(waitqueue, &wait.wait); } EXPORT_SYMBOL(wait_on_page_bit); @@ -334,13 +377,11 @@ EXPORT_SYMBOL(wait_on_page_bit); */ void fastcall unlock_page(struct page *page) { - wait_queue_head_t *waitqueue = page_waitqueue(page); smp_mb__before_clear_bit(); if (!TestClearPageLocked(page)) BUG(); smp_mb__after_clear_bit(); - if (waitqueue_active(waitqueue)) - wake_up_all(waitqueue); + wake_up_page(page); } EXPORT_SYMBOL(unlock_page); @@ -351,15 +392,12 @@ EXPORT_SYMBOL(lock_page); */ void end_page_writeback(struct page *page) { - wait_queue_head_t *waitqueue = page_waitqueue(page); - if (!TestClearPageReclaim(page) || rotate_reclaimable_page(page)) { if (!test_clear_page_writeback(page)) BUG(); smp_mb__after_clear_bit(); } - if (waitqueue_active(waitqueue)) - wake_up_all(waitqueue); + wake_up_page(page); } EXPORT_SYMBOL(end_page_writeback); @@ -375,16 +413,16 @@ EXPORT_SYMBOL(end_page_writeback); void fastcall __lock_page(struct page *page) { wait_queue_head_t *wqh = page_waitqueue(page); - DEFINE_WAIT(wait); + DEFINE_PAGE_WAIT_EXCLUSIVE(wait, page, PG_locked); while (TestSetPageLocked(page)) { - prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE); + prepare_to_wait_exclusive(wqh, &wait.wait, TASK_UNINTERRUPTIBLE); if (PageLocked(page)) { sync_page(page); io_schedule(); } } - finish_wait(wqh, &wait); + finish_wait(wqh, &wait.wait); } EXPORT_SYMBOL(__lock_page); _