diff options
Diffstat (limited to 'mm/swapfile.c')
-rw-r--r-- | mm/swapfile.c | 31 |
1 files changed, 27 insertions, 4 deletions
diff --git a/mm/swapfile.c b/mm/swapfile.c index dac47c66055c..98e52e373bd8 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c | |||
@@ -370,7 +370,8 @@ static void dec_cluster_info_page(struct swap_info_struct *p, | |||
370 | * instead of free it immediately. The cluster will be freed | 370 | * instead of free it immediately. The cluster will be freed |
371 | * after discard. | 371 | * after discard. |
372 | */ | 372 | */ |
373 | if (p->flags & SWP_PAGE_DISCARD) { | 373 | if ((p->flags & (SWP_WRITEOK | SWP_PAGE_DISCARD)) == |
374 | (SWP_WRITEOK | SWP_PAGE_DISCARD)) { | ||
374 | swap_cluster_schedule_discard(p, idx); | 375 | swap_cluster_schedule_discard(p, idx); |
375 | return; | 376 | return; |
376 | } | 377 | } |
@@ -1288,7 +1289,7 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si, | |||
1288 | else | 1289 | else |
1289 | continue; | 1290 | continue; |
1290 | } | 1291 | } |
1291 | count = si->swap_map[i]; | 1292 | count = ACCESS_ONCE(si->swap_map[i]); |
1292 | if (count && swap_count(count) != SWAP_MAP_BAD) | 1293 | if (count && swap_count(count) != SWAP_MAP_BAD) |
1293 | break; | 1294 | break; |
1294 | } | 1295 | } |
@@ -1308,7 +1309,11 @@ int try_to_unuse(unsigned int type, bool frontswap, | |||
1308 | { | 1309 | { |
1309 | struct swap_info_struct *si = swap_info[type]; | 1310 | struct swap_info_struct *si = swap_info[type]; |
1310 | struct mm_struct *start_mm; | 1311 | struct mm_struct *start_mm; |
1311 | unsigned char *swap_map; | 1312 | volatile unsigned char *swap_map; /* swap_map is accessed without |
1313 | * locking. Mark it as volatile | ||
1314 | * to prevent compiler doing | ||
1315 | * something odd. | ||
1316 | */ | ||
1312 | unsigned char swcount; | 1317 | unsigned char swcount; |
1313 | struct page *page; | 1318 | struct page *page; |
1314 | swp_entry_t entry; | 1319 | swp_entry_t entry; |
@@ -1359,7 +1364,15 @@ int try_to_unuse(unsigned int type, bool frontswap, | |||
1359 | * reused since sys_swapoff() already disabled | 1364 | * reused since sys_swapoff() already disabled |
1360 | * allocation from here, or alloc_page() failed. | 1365 | * allocation from here, or alloc_page() failed. |
1361 | */ | 1366 | */ |
1362 | if (!*swap_map) | 1367 | swcount = *swap_map; |
1368 | /* | ||
1369 | * We don't hold lock here, so the swap entry could be | ||
1370 | * SWAP_MAP_BAD (when the cluster is discarding). | ||
1371 | * Instead of fail out, We can just skip the swap | ||
1372 | * entry because swapoff will wait for discarding | ||
1373 | * finish anyway. | ||
1374 | */ | ||
1375 | if (!swcount || swcount == SWAP_MAP_BAD) | ||
1363 | continue; | 1376 | continue; |
1364 | retval = -ENOMEM; | 1377 | retval = -ENOMEM; |
1365 | break; | 1378 | break; |
@@ -2543,6 +2556,16 @@ static int __swap_duplicate(swp_entry_t entry, unsigned char usage) | |||
2543 | goto unlock_out; | 2556 | goto unlock_out; |
2544 | 2557 | ||
2545 | count = p->swap_map[offset]; | 2558 | count = p->swap_map[offset]; |
2559 | |||
2560 | /* | ||
2561 | * swapin_readahead() doesn't check if a swap entry is valid, so the | ||
2562 | * swap entry could be SWAP_MAP_BAD. Check here with lock held. | ||
2563 | */ | ||
2564 | if (unlikely(swap_count(count) == SWAP_MAP_BAD)) { | ||
2565 | err = -ENOENT; | ||
2566 | goto unlock_out; | ||
2567 | } | ||
2568 | |||
2546 | has_cache = count & SWAP_HAS_CACHE; | 2569 | has_cache = count & SWAP_HAS_CACHE; |
2547 | count &= ~SWAP_HAS_CACHE; | 2570 | count &= ~SWAP_HAS_CACHE; |
2548 | err = 0; | 2571 | err = 0; |