[PATCH] unpaged: COW on VM_UNPAGED

Remove the BUG_ON(vma->vm_flags & VM_UNPAGED) from do_wp_page, and let it do
Copy-On-Write without touching the VM_UNPAGED's page counts - but this is
incomplete, because the anonymous page it inserts will itself need to be
handled, here and in other functions - next patch.

We still don't copy the page if the pfn is invalid, because the
copy_user_highpage interface does not allow it. But that's not been a problem
in the past: can be added in later if the need arises.

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

authored by Hugh Dickins and committed by Linus Torvalds 920fc356 101d2be7

+25 -12
+25 -12
mm/memory.c
··· 1277 1277 unsigned long address, pte_t *page_table, pmd_t *pmd, 1278 1278 spinlock_t *ptl, pte_t orig_pte) 1279 1279 { 1280 - struct page *old_page, *new_page; 1280 + struct page *old_page, *src_page, *new_page; 1281 1281 unsigned long pfn = pte_pfn(orig_pte); 1282 1282 pte_t entry; 1283 1283 int ret = VM_FAULT_MINOR; 1284 1284 1285 - BUG_ON(vma->vm_flags & VM_UNPAGED); 1286 - 1287 1285 if (unlikely(!pfn_valid(pfn))) { 1288 1286 /* 1289 1287 * Page table corrupted: show pte and kill process. 1288 + * Or it's an attempt to COW an out-of-map VM_UNPAGED 1289 + * entry, which copy_user_highpage does not support. 1290 1290 */ 1291 1291 print_bad_pte(vma, orig_pte, address); 1292 1292 ret = VM_FAULT_OOM; 1293 1293 goto unlock; 1294 1294 } 1295 1295 old_page = pfn_to_page(pfn); 1296 + src_page = old_page; 1297 + 1298 + if (unlikely(vma->vm_flags & VM_UNPAGED)) { 1299 + old_page = NULL; 1300 + goto gotten; 1301 + } 1296 1302 1297 1303 if (PageAnon(old_page) && !TestSetPageLocked(old_page)) { 1298 1304 int reuse = can_share_swap_page(old_page); ··· 1319 1313 * Ok, we need to copy. Oh, well.. 1320 1314 */ 1321 1315 page_cache_get(old_page); 1316 + gotten: 1322 1317 pte_unmap_unlock(page_table, ptl); 1323 1318 1324 1319 if (unlikely(anon_vma_prepare(vma))) 1325 1320 goto oom; 1326 - if (old_page == ZERO_PAGE(address)) { 1321 + if (src_page == ZERO_PAGE(address)) { 1327 1322 new_page = alloc_zeroed_user_highpage(vma, address); 1328 1323 if (!new_page) 1329 1324 goto oom; ··· 1332 1325 new_page = alloc_page_vma(GFP_HIGHUSER, vma, address); 1333 1326 if (!new_page) 1334 1327 goto oom; 1335 - copy_user_highpage(new_page, old_page, address); 1328 + copy_user_highpage(new_page, src_page, address); 1336 1329 } 1337 1330 1338 1331 /* ··· 1340 1333 */ 1341 1334 page_table = pte_offset_map_lock(mm, pmd, address, &ptl); 1342 1335 if (likely(pte_same(*page_table, orig_pte))) { 1343 - page_remove_rmap(old_page); 1344 - if (!PageAnon(old_page)) { 1336 + if (old_page) { 1337 + page_remove_rmap(old_page); 1338 + if (!PageAnon(old_page)) { 1339 + dec_mm_counter(mm, file_rss); 1340 + inc_mm_counter(mm, anon_rss); 1341 + } 1342 + } else 1345 1343 inc_mm_counter(mm, anon_rss); 1346 - dec_mm_counter(mm, file_rss); 1347 - } 1348 1344 flush_cache_page(vma, address, pfn); 1349 1345 entry = mk_pte(new_page, vma->vm_page_prot); 1350 1346 entry = maybe_mkwrite(pte_mkdirty(entry), vma); ··· 1361 1351 new_page = old_page; 1362 1352 ret |= VM_FAULT_WRITE; 1363 1353 } 1364 - page_cache_release(new_page); 1365 - page_cache_release(old_page); 1354 + if (new_page) 1355 + page_cache_release(new_page); 1356 + if (old_page) 1357 + page_cache_release(old_page); 1366 1358 unlock: 1367 1359 pte_unmap_unlock(page_table, ptl); 1368 1360 return ret; 1369 1361 oom: 1370 - page_cache_release(old_page); 1362 + if (old_page) 1363 + page_cache_release(old_page); 1371 1364 return VM_FAULT_OOM; 1372 1365 } 1373 1366