Commit 008f707c authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] batched removal of pages from the LRU

Convert all the bulk callers of lru_cache_del() to use the batched
pagevec_lru_del() function.

Change truncate_complete_page() to not delete the page from the LRU.
Do it in page_cache_release() instead.  (This reintroduces the problem
with final-release-from-interrupt.  THat gets fixed further on).

This patch changes the truncate locking somewhat.  The removal from the
LRU now happens _after_ the page has been removed from the
address_space and has been unlocked.  So there is now a window where
the shrink_cache code can discover the to-be-freed page via the LRU
list.  But that's OK - the page is clean, its buffers (if any) are
clean.  It's not attached to any mapping.
parent 9eb76ee2
...@@ -118,10 +118,10 @@ void invalidate_inode_pages(struct inode * inode) ...@@ -118,10 +118,10 @@ void invalidate_inode_pages(struct inode * inode)
struct list_head *head, *curr; struct list_head *head, *curr;
struct page * page; struct page * page;
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
struct pagevec lru_pvec;
head = &mapping->clean_pages; head = &mapping->clean_pages;
pagevec_init(&lru_pvec);
spin_lock(&pagemap_lru_lock);
write_lock(&mapping->page_lock); write_lock(&mapping->page_lock);
curr = head->next; curr = head->next;
...@@ -143,10 +143,10 @@ void invalidate_inode_pages(struct inode * inode) ...@@ -143,10 +143,10 @@ void invalidate_inode_pages(struct inode * inode)
if (page_count(page) != 1) if (page_count(page) != 1)
goto unlock; goto unlock;
__lru_cache_del(page);
__remove_from_page_cache(page); __remove_from_page_cache(page);
unlock_page(page); unlock_page(page);
page_cache_release(page); if (!pagevec_add(&lru_pvec, page))
__pagevec_lru_del(&lru_pvec);
continue; continue;
unlock: unlock:
unlock_page(page); unlock_page(page);
...@@ -154,7 +154,7 @@ void invalidate_inode_pages(struct inode * inode) ...@@ -154,7 +154,7 @@ void invalidate_inode_pages(struct inode * inode)
} }
write_unlock(&mapping->page_lock); write_unlock(&mapping->page_lock);
spin_unlock(&pagemap_lru_lock); pagevec_lru_del(&lru_pvec);
} }
static int do_invalidatepage(struct page *page, unsigned long offset) static int do_invalidatepage(struct page *page, unsigned long offset)
...@@ -174,16 +174,14 @@ static inline void truncate_partial_page(struct page *page, unsigned partial) ...@@ -174,16 +174,14 @@ static inline void truncate_partial_page(struct page *page, unsigned partial)
} }
/* /*
* If truncate can remove the fs-private metadata from the page, it * If truncate cannot remove the fs-private metadata from the page, the page
* removes the page from the LRU immediately. This because some other thread * becomes anonymous. It will be left on the LRU and may even be mapped into
* of control (eg, sendfile) may have a reference to the page. But dropping * user pagetables if we're racing with filemap_nopage().
* the final reference to an LRU page in interrupt context is illegal - it may
* deadlock over pagemap_lru_lock.
*/ */
static void truncate_complete_page(struct page *page) static void truncate_complete_page(struct page *page)
{ {
if (!PagePrivate(page) || do_invalidatepage(page, 0)) if (PagePrivate(page))
lru_cache_del(page); do_invalidatepage(page, 0);
ClearPageDirty(page); ClearPageDirty(page);
ClearPageUptodate(page); ClearPageUptodate(page);
...@@ -204,8 +202,10 @@ static int truncate_list_pages(struct address_space *mapping, ...@@ -204,8 +202,10 @@ static int truncate_list_pages(struct address_space *mapping,
struct list_head *curr; struct list_head *curr;
struct page * page; struct page * page;
int unlocked = 0; int unlocked = 0;
struct pagevec release_pvec;
restart: pagevec_init(&release_pvec);
restart:
curr = head->next; curr = head->next;
while (curr != head) { while (curr != head) {
unsigned long offset; unsigned long offset;
...@@ -225,18 +225,17 @@ static int truncate_list_pages(struct address_space *mapping, ...@@ -225,18 +225,17 @@ static int truncate_list_pages(struct address_space *mapping,
list_add_tail(head, curr); list_add_tail(head, curr);
write_unlock(&mapping->page_lock); write_unlock(&mapping->page_lock);
wait_on_page_writeback(page); wait_on_page_writeback(page);
page_cache_release(page); if (!pagevec_add(&release_pvec, page))
__pagevec_release(&release_pvec);
unlocked = 1; unlocked = 1;
write_lock(&mapping->page_lock); write_lock(&mapping->page_lock);
goto restart; goto restart;
} }
list_del(head); list_del(head);
if (!failed) if (!failed) /* Restart after this page */
/* Restart after this page */
list_add(head, curr); list_add(head, curr);
else else /* Restart on this page */
/* Restart on this page */
list_add_tail(head, curr); list_add_tail(head, curr);
write_unlock(&mapping->page_lock); write_unlock(&mapping->page_lock);
...@@ -246,25 +245,27 @@ static int truncate_list_pages(struct address_space *mapping, ...@@ -246,25 +245,27 @@ static int truncate_list_pages(struct address_space *mapping,
if (*partial && (offset + 1) == start) { if (*partial && (offset + 1) == start) {
truncate_partial_page(page, *partial); truncate_partial_page(page, *partial);
*partial = 0; *partial = 0;
} else } else {
truncate_complete_page(page); truncate_complete_page(page);
}
unlock_page(page); unlock_page(page);
} else } else {
wait_on_page_locked(page); wait_on_page_locked(page);
page_cache_release(page);
if (need_resched()) {
__set_current_state(TASK_RUNNING);
schedule();
} }
if (!pagevec_add(&release_pvec, page))
__pagevec_release(&release_pvec);
cond_resched();
write_lock(&mapping->page_lock); write_lock(&mapping->page_lock);
goto restart; goto restart;
} }
curr = curr->next; curr = curr->next;
} }
if (pagevec_count(&release_pvec)) {
write_unlock(&mapping->page_lock);
pagevec_release(&release_pvec);
write_lock(&mapping->page_lock);
unlocked = 1;
}
return unlocked; return unlocked;
} }
...@@ -362,8 +363,10 @@ static int invalidate_list_pages2(struct address_space * mapping, ...@@ -362,8 +363,10 @@ static int invalidate_list_pages2(struct address_space * mapping,
struct list_head *curr; struct list_head *curr;
struct page * page; struct page * page;
int unlocked = 0; int unlocked = 0;
struct pagevec release_pvec;
restart: pagevec_init(&release_pvec);
restart:
curr = head->prev; curr = head->prev;
while (curr != head) { while (curr != head) {
page = list_entry(curr, struct page, list); page = list_entry(curr, struct page, list);
...@@ -380,7 +383,8 @@ static int invalidate_list_pages2(struct address_space * mapping, ...@@ -380,7 +383,8 @@ static int invalidate_list_pages2(struct address_space * mapping,
goto restart; goto restart;
} }
__unlocked = invalidate_this_page2(mapping, page, curr, head); __unlocked = invalidate_this_page2(mapping,
page, curr, head);
unlock_page(page); unlock_page(page);
unlocked |= __unlocked; unlocked |= __unlocked;
if (!__unlocked) { if (!__unlocked) {
...@@ -398,15 +402,18 @@ static int invalidate_list_pages2(struct address_space * mapping, ...@@ -398,15 +402,18 @@ static int invalidate_list_pages2(struct address_space * mapping,
wait_on_page_locked(page); wait_on_page_locked(page);
} }
page_cache_release(page); if (!pagevec_add(&release_pvec, page))
if (need_resched()) { __pagevec_release(&release_pvec);
__set_current_state(TASK_RUNNING); cond_resched();
schedule();
}
write_lock(&mapping->page_lock); write_lock(&mapping->page_lock);
goto restart; goto restart;
} }
if (pagevec_count(&release_pvec)) {
write_unlock(&mapping->page_lock);
pagevec_release(&release_pvec);
write_lock(&mapping->page_lock);
unlocked = 1;
}
return unlocked; return unlocked;
} }
......
...@@ -115,7 +115,7 @@ void __pagevec_release(struct pagevec *pvec) ...@@ -115,7 +115,7 @@ void __pagevec_release(struct pagevec *pvec)
if (!put_page_testzero(page)) if (!put_page_testzero(page))
continue; continue;
if (!lock_held && PageLRU(page)) { if (!lock_held) {
spin_lock(&pagemap_lru_lock); spin_lock(&pagemap_lru_lock);
lock_held = 1; lock_held = 1;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment