Commit e8788c0cce63e0cc8689a123d1ce0af1e28cd583

Authored by Christoph Lameter
Committed by Linus Torvalds
1 parent 6af6aab34a

[PATCH] remove_from_swap: fix locking

remove_from_swap() currently attempts to use page_lock_anon_vma to obtain
an anon_vma lock.  That is not working since the page may have been
remapped via swap ptes in order to move the page.

However, do_migrate_pages() obtain the mmap_sem lock and therefore there is
a guarantee that the anonymous vma will not vanish from under us.  There is
therefore no need to use page_lock_anon_vma.

Signed-off-by: Christoph Lameter <clameter@sgi.com>
Acked-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 1 changed file with 13 additions and 5 deletions Side-by-side Diff

... ... @@ -212,25 +212,33 @@
212 212 * through real pte's pointing to valid pages and then releasing
213 213 * the page from the swap cache.
214 214 *
215   - * Must hold page lock on page.
  215 + * Must hold page lock on page and mmap_sem of one vma that contains
  216 + * the page.
216 217 */
217 218 void remove_from_swap(struct page *page)
218 219 {
219 220 struct anon_vma *anon_vma;
220 221 struct vm_area_struct *vma;
  222 + unsigned long mapping;
221 223  
222   - if (!PageAnon(page) || !PageSwapCache(page))
  224 + if (!PageSwapCache(page))
223 225 return;
224 226  
225   - anon_vma = page_lock_anon_vma(page);
226   - if (!anon_vma)
  227 + mapping = (unsigned long)page->mapping;
  228 +
  229 + if (!mapping || (mapping & PAGE_MAPPING_ANON) == 0)
227 230 return;
228 231  
  232 + /*
  233 + * We hold the mmap_sem lock. So no need to call page_lock_anon_vma.
  234 + */
  235 + anon_vma = (struct anon_vma *) (mapping - PAGE_MAPPING_ANON);
  236 + spin_lock(&anon_vma->lock);
  237 +
229 238 list_for_each_entry(vma, &anon_vma->head, anon_vma_node)
230 239 remove_vma_swap(vma, page);
231 240  
232 241 spin_unlock(&anon_vma->lock);
233   -
234 242 delete_from_swap_cache(page);
235 243 }
236 244 EXPORT_SYMBOL(remove_from_swap);