diff options
author | Hugh Dickins <hughd@google.com> | 2011-07-25 20:12:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-25 23:57:11 -0400 |
commit | 27ab700626f048407e9466d389a43c7d3aa45967 (patch) | |
tree | a8d371d8a3727110310cba457840be0f2cfb77c6 /mm/shmem.c | |
parent | e83c32e8f92724a06a22a3b42f3afc07db93e131 (diff) |
tmpfs: simplify filepage/swappage
We can now simplify shmem_getpage_gfp(): there is no longer a dilemma of
filepage passed in via shmem_readpage(), then swappage found, which must
then be copied over to it.
Although at first it's tempting to replace the **pagep arg by returning
struct page *, that makes a mess of IS_ERR_OR_NULL(page)s in all the
callers, so leave as is.
Insert BUG_ON(!PageUptodate) when we find and lock page: some of the
complication came from uninitialized pages inserted into filecache prior
to readpage; but now we're in control, and only release pagelock on
filecache once it's uptodate (if an error occurs in reading back from
swap, the page remains in swapcache, never moved to filecache).
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/shmem.c')
-rw-r--r-- | mm/shmem.c | 237 |
1 files changed, 108 insertions, 129 deletions
diff --git a/mm/shmem.c b/mm/shmem.c index 8f8534f35476..bf6e9c11d859 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -1246,41 +1246,47 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t idx, | |||
1246 | struct address_space *mapping = inode->i_mapping; | 1246 | struct address_space *mapping = inode->i_mapping; |
1247 | struct shmem_inode_info *info = SHMEM_I(inode); | 1247 | struct shmem_inode_info *info = SHMEM_I(inode); |
1248 | struct shmem_sb_info *sbinfo; | 1248 | struct shmem_sb_info *sbinfo; |
1249 | struct page *filepage; | 1249 | struct page *page; |
1250 | struct page *swappage; | ||
1251 | struct page *prealloc_page = NULL; | 1250 | struct page *prealloc_page = NULL; |
1252 | swp_entry_t *entry; | 1251 | swp_entry_t *entry; |
1253 | swp_entry_t swap; | 1252 | swp_entry_t swap; |
1254 | int error; | 1253 | int error; |
1254 | int ret; | ||
1255 | 1255 | ||
1256 | if (idx >= SHMEM_MAX_INDEX) | 1256 | if (idx >= SHMEM_MAX_INDEX) |
1257 | return -EFBIG; | 1257 | return -EFBIG; |
1258 | repeat: | 1258 | repeat: |
1259 | filepage = find_lock_page(mapping, idx); | 1259 | page = find_lock_page(mapping, idx); |
1260 | if (filepage && PageUptodate(filepage)) | 1260 | if (page) { |
1261 | goto done; | ||
1262 | if (!filepage) { | ||
1263 | /* | 1261 | /* |
1264 | * Try to preload while we can wait, to not make a habit of | 1262 | * Once we can get the page lock, it must be uptodate: |
1265 | * draining atomic reserves; but don't latch on to this cpu. | 1263 | * if there were an error in reading back from swap, |
1264 | * the page would not be inserted into the filecache. | ||
1266 | */ | 1265 | */ |
1267 | error = radix_tree_preload(gfp & GFP_RECLAIM_MASK); | 1266 | BUG_ON(!PageUptodate(page)); |
1268 | if (error) | 1267 | goto done; |
1269 | goto failed; | 1268 | } |
1270 | radix_tree_preload_end(); | 1269 | |
1271 | if (sgp != SGP_READ && !prealloc_page) { | 1270 | /* |
1272 | prealloc_page = shmem_alloc_page(gfp, info, idx); | 1271 | * Try to preload while we can wait, to not make a habit of |
1273 | if (prealloc_page) { | 1272 | * draining atomic reserves; but don't latch on to this cpu. |
1274 | SetPageSwapBacked(prealloc_page); | 1273 | */ |
1275 | if (mem_cgroup_cache_charge(prealloc_page, | 1274 | error = radix_tree_preload(gfp & GFP_RECLAIM_MASK); |
1276 | current->mm, GFP_KERNEL)) { | 1275 | if (error) |
1277 | page_cache_release(prealloc_page); | 1276 | goto out; |
1278 | prealloc_page = NULL; | 1277 | radix_tree_preload_end(); |
1279 | } | 1278 | |
1279 | if (sgp != SGP_READ && !prealloc_page) { | ||
1280 | prealloc_page = shmem_alloc_page(gfp, info, idx); | ||
1281 | if (prealloc_page) { | ||
1282 | SetPageSwapBacked(prealloc_page); | ||
1283 | if (mem_cgroup_cache_charge(prealloc_page, | ||
1284 | current->mm, GFP_KERNEL)) { | ||
1285 | page_cache_release(prealloc_page); | ||
1286 | prealloc_page = NULL; | ||
1280 | } | 1287 | } |
1281 | } | 1288 | } |
1282 | } | 1289 | } |
1283 | error = 0; | ||
1284 | 1290 | ||
1285 | spin_lock(&info->lock); | 1291 | spin_lock(&info->lock); |
1286 | shmem_recalc_inode(inode); | 1292 | shmem_recalc_inode(inode); |
@@ -1288,21 +1294,21 @@ repeat: | |||
1288 | if (IS_ERR(entry)) { | 1294 | if (IS_ERR(entry)) { |
1289 | spin_unlock(&info->lock); | 1295 | spin_unlock(&info->lock); |
1290 | error = PTR_ERR(entry); | 1296 | error = PTR_ERR(entry); |
1291 | goto failed; | 1297 | goto out; |
1292 | } | 1298 | } |
1293 | swap = *entry; | 1299 | swap = *entry; |
1294 | 1300 | ||
1295 | if (swap.val) { | 1301 | if (swap.val) { |
1296 | /* Look it up and read it in.. */ | 1302 | /* Look it up and read it in.. */ |
1297 | swappage = lookup_swap_cache(swap); | 1303 | page = lookup_swap_cache(swap); |
1298 | if (!swappage) { | 1304 | if (!page) { |
1299 | shmem_swp_unmap(entry); | 1305 | shmem_swp_unmap(entry); |
1300 | spin_unlock(&info->lock); | 1306 | spin_unlock(&info->lock); |
1301 | /* here we actually do the io */ | 1307 | /* here we actually do the io */ |
1302 | if (fault_type) | 1308 | if (fault_type) |
1303 | *fault_type |= VM_FAULT_MAJOR; | 1309 | *fault_type |= VM_FAULT_MAJOR; |
1304 | swappage = shmem_swapin(swap, gfp, info, idx); | 1310 | page = shmem_swapin(swap, gfp, info, idx); |
1305 | if (!swappage) { | 1311 | if (!page) { |
1306 | spin_lock(&info->lock); | 1312 | spin_lock(&info->lock); |
1307 | entry = shmem_swp_alloc(info, idx, sgp, gfp); | 1313 | entry = shmem_swp_alloc(info, idx, sgp, gfp); |
1308 | if (IS_ERR(entry)) | 1314 | if (IS_ERR(entry)) |
@@ -1314,62 +1320,42 @@ repeat: | |||
1314 | } | 1320 | } |
1315 | spin_unlock(&info->lock); | 1321 | spin_unlock(&info->lock); |
1316 | if (error) | 1322 | if (error) |
1317 | goto failed; | 1323 | goto out; |
1318 | goto repeat; | 1324 | goto repeat; |
1319 | } | 1325 | } |
1320 | wait_on_page_locked(swappage); | 1326 | wait_on_page_locked(page); |
1321 | page_cache_release(swappage); | 1327 | page_cache_release(page); |
1322 | goto repeat; | 1328 | goto repeat; |
1323 | } | 1329 | } |
1324 | 1330 | ||
1325 | /* We have to do this with page locked to prevent races */ | 1331 | /* We have to do this with page locked to prevent races */ |
1326 | if (!trylock_page(swappage)) { | 1332 | if (!trylock_page(page)) { |
1327 | shmem_swp_unmap(entry); | 1333 | shmem_swp_unmap(entry); |
1328 | spin_unlock(&info->lock); | 1334 | spin_unlock(&info->lock); |
1329 | wait_on_page_locked(swappage); | 1335 | wait_on_page_locked(page); |
1330 | page_cache_release(swappage); | 1336 | page_cache_release(page); |
1331 | goto repeat; | 1337 | goto repeat; |
1332 | } | 1338 | } |
1333 | if (PageWriteback(swappage)) { | 1339 | if (PageWriteback(page)) { |
1334 | shmem_swp_unmap(entry); | 1340 | shmem_swp_unmap(entry); |
1335 | spin_unlock(&info->lock); | 1341 | spin_unlock(&info->lock); |
1336 | wait_on_page_writeback(swappage); | 1342 | wait_on_page_writeback(page); |
1337 | unlock_page(swappage); | 1343 | unlock_page(page); |
1338 | page_cache_release(swappage); | 1344 | page_cache_release(page); |
1339 | goto repeat; | 1345 | goto repeat; |
1340 | } | 1346 | } |
1341 | if (!PageUptodate(swappage)) { | 1347 | if (!PageUptodate(page)) { |
1342 | shmem_swp_unmap(entry); | 1348 | shmem_swp_unmap(entry); |
1343 | spin_unlock(&info->lock); | 1349 | spin_unlock(&info->lock); |
1344 | unlock_page(swappage); | 1350 | unlock_page(page); |
1345 | page_cache_release(swappage); | 1351 | page_cache_release(page); |
1346 | error = -EIO; | 1352 | error = -EIO; |
1347 | goto failed; | 1353 | goto out; |
1348 | } | 1354 | } |
1349 | 1355 | ||
1350 | if (filepage) { | 1356 | error = add_to_page_cache_locked(page, mapping, |
1351 | shmem_swp_set(info, entry, 0); | 1357 | idx, GFP_NOWAIT); |
1352 | shmem_swp_unmap(entry); | 1358 | if (error) { |
1353 | delete_from_swap_cache(swappage); | ||
1354 | spin_unlock(&info->lock); | ||
1355 | copy_highpage(filepage, swappage); | ||
1356 | unlock_page(swappage); | ||
1357 | page_cache_release(swappage); | ||
1358 | flush_dcache_page(filepage); | ||
1359 | SetPageUptodate(filepage); | ||
1360 | set_page_dirty(filepage); | ||
1361 | swap_free(swap); | ||
1362 | } else if (!(error = add_to_page_cache_locked(swappage, mapping, | ||
1363 | idx, GFP_NOWAIT))) { | ||
1364 | info->flags |= SHMEM_PAGEIN; | ||
1365 | shmem_swp_set(info, entry, 0); | ||
1366 | shmem_swp_unmap(entry); | ||
1367 | delete_from_swap_cache(swappage); | ||
1368 | spin_unlock(&info->lock); | ||
1369 | filepage = swappage; | ||
1370 | set_page_dirty(filepage); | ||
1371 | swap_free(swap); | ||
1372 | } else { | ||
1373 | shmem_swp_unmap(entry); | 1359 | shmem_swp_unmap(entry); |
1374 | spin_unlock(&info->lock); | 1360 | spin_unlock(&info->lock); |
1375 | if (error == -ENOMEM) { | 1361 | if (error == -ENOMEM) { |
@@ -1378,28 +1364,33 @@ repeat: | |||
1378 | * call memcg's OOM if needed. | 1364 | * call memcg's OOM if needed. |
1379 | */ | 1365 | */ |
1380 | error = mem_cgroup_shmem_charge_fallback( | 1366 | error = mem_cgroup_shmem_charge_fallback( |
1381 | swappage, | 1367 | page, current->mm, gfp); |
1382 | current->mm, | ||
1383 | gfp); | ||
1384 | if (error) { | 1368 | if (error) { |
1385 | unlock_page(swappage); | 1369 | unlock_page(page); |
1386 | page_cache_release(swappage); | 1370 | page_cache_release(page); |
1387 | goto failed; | 1371 | goto out; |
1388 | } | 1372 | } |
1389 | } | 1373 | } |
1390 | unlock_page(swappage); | 1374 | unlock_page(page); |
1391 | page_cache_release(swappage); | 1375 | page_cache_release(page); |
1392 | goto repeat; | 1376 | goto repeat; |
1393 | } | 1377 | } |
1394 | } else if (sgp == SGP_READ && !filepage) { | 1378 | |
1379 | info->flags |= SHMEM_PAGEIN; | ||
1380 | shmem_swp_set(info, entry, 0); | ||
1395 | shmem_swp_unmap(entry); | 1381 | shmem_swp_unmap(entry); |
1396 | filepage = find_get_page(mapping, idx); | 1382 | delete_from_swap_cache(page); |
1397 | if (filepage && | 1383 | spin_unlock(&info->lock); |
1398 | (!PageUptodate(filepage) || !trylock_page(filepage))) { | 1384 | set_page_dirty(page); |
1385 | swap_free(swap); | ||
1386 | |||
1387 | } else if (sgp == SGP_READ) { | ||
1388 | shmem_swp_unmap(entry); | ||
1389 | page = find_get_page(mapping, idx); | ||
1390 | if (page && !trylock_page(page)) { | ||
1399 | spin_unlock(&info->lock); | 1391 | spin_unlock(&info->lock); |
1400 | wait_on_page_locked(filepage); | 1392 | wait_on_page_locked(page); |
1401 | page_cache_release(filepage); | 1393 | page_cache_release(page); |
1402 | filepage = NULL; | ||
1403 | goto repeat; | 1394 | goto repeat; |
1404 | } | 1395 | } |
1405 | spin_unlock(&info->lock); | 1396 | spin_unlock(&info->lock); |
@@ -1417,56 +1408,52 @@ repeat: | |||
1417 | } else if (shmem_acct_block(info->flags)) | 1408 | } else if (shmem_acct_block(info->flags)) |
1418 | goto nospace; | 1409 | goto nospace; |
1419 | 1410 | ||
1420 | if (!filepage) { | 1411 | page = prealloc_page; |
1421 | int ret; | 1412 | prealloc_page = NULL; |
1422 | 1413 | ||
1423 | filepage = prealloc_page; | 1414 | entry = shmem_swp_alloc(info, idx, sgp, gfp); |
1424 | prealloc_page = NULL; | 1415 | if (IS_ERR(entry)) |
1425 | 1416 | error = PTR_ERR(entry); | |
1426 | entry = shmem_swp_alloc(info, idx, sgp, gfp); | 1417 | else { |
1427 | if (IS_ERR(entry)) | 1418 | swap = *entry; |
1428 | error = PTR_ERR(entry); | 1419 | shmem_swp_unmap(entry); |
1429 | else { | 1420 | } |
1430 | swap = *entry; | 1421 | ret = error || swap.val; |
1431 | shmem_swp_unmap(entry); | 1422 | if (ret) |
1432 | } | 1423 | mem_cgroup_uncharge_cache_page(page); |
1433 | ret = error || swap.val; | 1424 | else |
1434 | if (ret) | 1425 | ret = add_to_page_cache_lru(page, mapping, |
1435 | mem_cgroup_uncharge_cache_page(filepage); | ||
1436 | else | ||
1437 | ret = add_to_page_cache_lru(filepage, mapping, | ||
1438 | idx, GFP_NOWAIT); | 1426 | idx, GFP_NOWAIT); |
1439 | /* | 1427 | /* |
1440 | * At add_to_page_cache_lru() failure, uncharge will | 1428 | * At add_to_page_cache_lru() failure, |
1441 | * be done automatically. | 1429 | * uncharge will be done automatically. |
1442 | */ | 1430 | */ |
1443 | if (ret) { | 1431 | if (ret) { |
1444 | shmem_unacct_blocks(info->flags, 1); | 1432 | shmem_unacct_blocks(info->flags, 1); |
1445 | shmem_free_blocks(inode, 1); | 1433 | shmem_free_blocks(inode, 1); |
1446 | spin_unlock(&info->lock); | 1434 | spin_unlock(&info->lock); |
1447 | page_cache_release(filepage); | 1435 | page_cache_release(page); |
1448 | filepage = NULL; | 1436 | if (error) |
1449 | if (error) | 1437 | goto out; |
1450 | goto failed; | 1438 | goto repeat; |
1451 | goto repeat; | ||
1452 | } | ||
1453 | info->flags |= SHMEM_PAGEIN; | ||
1454 | } | 1439 | } |
1455 | 1440 | ||
1441 | info->flags |= SHMEM_PAGEIN; | ||
1456 | info->alloced++; | 1442 | info->alloced++; |
1457 | spin_unlock(&info->lock); | 1443 | spin_unlock(&info->lock); |
1458 | clear_highpage(filepage); | 1444 | clear_highpage(page); |
1459 | flush_dcache_page(filepage); | 1445 | flush_dcache_page(page); |
1460 | SetPageUptodate(filepage); | 1446 | SetPageUptodate(page); |
1461 | if (sgp == SGP_DIRTY) | 1447 | if (sgp == SGP_DIRTY) |
1462 | set_page_dirty(filepage); | 1448 | set_page_dirty(page); |
1449 | |||
1463 | } else { | 1450 | } else { |
1464 | spin_unlock(&info->lock); | 1451 | spin_unlock(&info->lock); |
1465 | error = -ENOMEM; | 1452 | error = -ENOMEM; |
1466 | goto out; | 1453 | goto out; |
1467 | } | 1454 | } |
1468 | done: | 1455 | done: |
1469 | *pagep = filepage; | 1456 | *pagep = page; |
1470 | error = 0; | 1457 | error = 0; |
1471 | out: | 1458 | out: |
1472 | if (prealloc_page) { | 1459 | if (prealloc_page) { |
@@ -1482,21 +1469,13 @@ nospace: | |||
1482 | * but must also avoid reporting a spurious ENOSPC while working on a | 1469 | * but must also avoid reporting a spurious ENOSPC while working on a |
1483 | * full tmpfs. | 1470 | * full tmpfs. |
1484 | */ | 1471 | */ |
1485 | if (!filepage) { | 1472 | page = find_get_page(mapping, idx); |
1486 | struct page *page = find_get_page(mapping, idx); | ||
1487 | if (page) { | ||
1488 | spin_unlock(&info->lock); | ||
1489 | page_cache_release(page); | ||
1490 | goto repeat; | ||
1491 | } | ||
1492 | } | ||
1493 | spin_unlock(&info->lock); | 1473 | spin_unlock(&info->lock); |
1494 | error = -ENOSPC; | 1474 | if (page) { |
1495 | failed: | 1475 | page_cache_release(page); |
1496 | if (filepage) { | 1476 | goto repeat; |
1497 | unlock_page(filepage); | ||
1498 | page_cache_release(filepage); | ||
1499 | } | 1477 | } |
1478 | error = -ENOSPC; | ||
1500 | goto out; | 1479 | goto out; |
1501 | } | 1480 | } |
1502 | 1481 | ||