Add swap_free_markbad(), equivalent to swap_free but marks the swap page
as bad. Update try_to_unuse_entry() and shmem_unuse() to call the new
function when working on pages with errors.
These patches are a based on a patch by Nick Piggin and some of my own
patches/bugfixes as discussed on LKML.
Signed-off-by: Richard Purdie <[email protected]>
---
include/linux/swap.h | 1 +
mm/shmem.c | 17 ++++++++++-------
mm/swap_state.c | 2 ++
mm/swapfile.c | 38 +++++++++++++++++++++++++++++---------
4 files changed, 42 insertions(+), 16 deletions(-)
Index: git/include/linux/swap.h
===================================================================
--- git.orig/include/linux/swap.h 2007-01-07 21:39:26.000000000 +0000
+++ git/include/linux/swap.h 2007-01-08 11:39:36.000000000 +0000
@@ -244,6 +244,7 @@ extern swp_entry_t get_swap_page_of_type
extern int swap_duplicate(swp_entry_t);
extern int valid_swaphandles(swp_entry_t, unsigned long *);
extern void swap_free(swp_entry_t);
+extern void swap_free_markbad(swp_entry_t);
extern void free_swap_and_cache(swp_entry_t);
extern int swap_type_of(dev_t, sector_t, struct block_device **);
extern unsigned int count_swap_pages(int, int);
Index: git/mm/shmem.c
===================================================================
--- git.orig/mm/shmem.c 2007-01-07 21:39:29.000000000 +0000
+++ git/mm/shmem.c 2007-01-08 11:39:36.000000000 +0000
@@ -734,7 +734,7 @@ static int shmem_unuse_inode(struct shme
struct page **dir;
struct page *subdir;
swp_entry_t *ptr;
- int offset;
+ int offset, moved, error;
idx = 0;
ptr = info->i_direct;
@@ -792,17 +792,20 @@ lost2:
found:
idx += offset;
inode = &info->vfs_inode;
- if (move_from_swap_cache(page, idx, inode->i_mapping) == 0) {
+ error = PageError(page);
+ moved = (move_from_swap_cache(page, idx, inode->i_mapping) == 0);
+ if (moved) {
info->flags |= SHMEM_PAGEIN;
shmem_swp_set(info, ptr + offset, 0);
}
shmem_swp_unmap(ptr);
spin_unlock(&info->lock);
- /*
- * Decrement swap count even when the entry is left behind:
- * try_to_unuse will skip over mms, then reincrement count.
- */
- swap_free(entry);
+ if (moved) {
+ if (!error)
+ swap_free(entry);
+ else
+ swap_free_markbad(entry);
+ }
return 1;
}
Index: git/mm/swapfile.c
===================================================================
--- git.orig/mm/swapfile.c 2007-01-08 11:39:27.000000000 +0000
+++ git/mm/swapfile.c 2007-01-08 11:39:36.000000000 +0000
@@ -304,6 +304,23 @@ void swap_free(swp_entry_t entry)
}
}
+void swap_free_markbad(swp_entry_t entry)
+{
+ struct swap_info_struct * p;
+
+ p = swap_info_get(entry);
+ if (p) {
+ unsigned long offset = swp_offset(entry);
+ if (swap_entry_free(p, offset) == 0) {
+ p->swap_map[offset] = SWAP_MAP_BAD;
+ p->pages--;
+ nr_swap_pages--;
+ total_swap_pages--;
+ }
+ spin_unlock(&swap_lock);
+ }
+}
+
/*
* How many references to page are currently swapped out?
*/
@@ -764,11 +781,6 @@ retry:
* remains (rarer), it will be read from disk into another page.
* Splitting into two pages would be incorrect if swap supported
* "shared private" pages, but they are handled by tmpfs files.
- *
- * Note shmem_unuse already deleted a swappage from the swap cache,
- * unless the move to filepage failed: in which case it left swappage
- * in cache, lowered its swap count to pass quickly through the loops
- * above, and now we must reincrement count to try again later.
*/
if (PageSwapCache(page)) {
if ((*swap_map > 1) && PageDirty(page)) {
@@ -781,11 +793,19 @@ retry:
wait_on_page_writeback(page);
goto retry;
}
+ if (!shmem) {
+ int error = PageError(page);
- if (shmem)
- swap_duplicate(entry);
- else
- delete_from_swap_cache(page);
+ write_lock_irq(&swapper_space.tree_lock);
+ __delete_from_swap_cache(page);
+ write_unlock_irq(&swapper_space.tree_lock);
+ page_cache_release(page); /* the swapcache ref */
+
+ if (!error)
+ swap_free(entry);
+ else
+ swap_free_markbad(entry);
+ }
}
/*
Index: git/mm/swap_state.c
===================================================================
--- git.orig/mm/swap_state.c 2007-01-07 21:39:29.000000000 +0000
+++ git/mm/swap_state.c 2007-01-08 11:39:36.000000000 +0000
@@ -131,6 +131,8 @@ void __delete_from_swap_cache(struct pag
radix_tree_delete(&swapper_space.page_tree, page_private(page));
set_page_private(page, 0);
ClearPageSwapCache(page);
+ if (unlikely(PageError(page)))
+ ClearPageError(page);
total_swapcache_pages--;
__dec_zone_page_state(page, NR_FILE_PAGES);
INC_CACHE_INFO(del_total);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Stuff]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
[Linux Resources]