samsung: snow: bitfix: Don't bail if we can't recover unused memory
authorDoug Anderson <dianders@chromium.org>
Mon, 18 Mar 2013 23:12:42 +0000 (16:12 -0700)
committerChromeBot <chrome-bot@google.com>
Wed, 20 Mar 2013 21:17:46 +0000 (14:17 -0700)
The bitfix code has the requirement that you can only recover an
entire 8K chunk of memory at a time, even though memory can be
"skipped" (not processed by bitfix) in 4K chunks.  If we ever detect
corruption in a 8K chunk that contained skipped pages then we'd bail
and reboot.

The original concept of "skipping" pages was there for sacred areas of
memory: the stack, console buffer, MMU structures, etc.  For these
areas of memory the above logic makes a lot of sense.  Dealing with a
page at a time meant that we could still _detect_ corruption on that
page.  ...and we could even fix it!  ...but since evidence suggested
that if one half of a chunk was corrupted that there's a good chance
of corruption in the other half, we chose to bail and reboot.

The above logic didn't take into account that we added another reason
for skipping a page: if it was unused.  For part of a chunk is unused
then we should just recover the half of the chunk that we care about
and not worry about corruption in the other half.

BUG=chromium-os:39522
TEST=suspend_stress_test

Change-Id: I57ad2fe24755e8867fdd4a5b729521b0ddb1d5fc
Signed-off-by: Doug Anderson <dianders@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/45807
Reviewed-by: Olof Johansson <olofj@chromium.org>
arch/arm/mach-exynos/bitfix-snow.c
arch/arm/plat-samsung/pm-check.c

index 8b1d175..aa03a9e 100644 (file)
@@ -404,13 +404,6 @@ static void _bitfix_recover_chunk(phys_addr_t failed_chunk,
        u32 cu;
        size_t offset;
 
-       /*
-        * If any of the pages in the failed chunk were skipped then we can't
-        * recover it; just bail.
-        */
-       for (offset = 0; offset < CHUNK_SIZE; offset += PAGE_SIZE)
-               BUG_ON(should_skip_fn(failed_chunk + offset));
-
        for (cu = 0; cu < CU_COUNT; cu++) {
                phys_addr_t this_chunk = (failed_chunk & ~CU_MASK) |
                        (cu << CU_OFFSET);
@@ -486,13 +479,37 @@ void bitfix_recover_chunk(phys_addr_t failed_addr,
        memset(recover_chunk, 0, CHUNK_SIZE);
        _bitfix_recover_chunk(bad_chunk_addr, should_skip_fn);
 
-       /* Do comparisons to characterize the corruption. */
+       /* Do comparisons to characterize the corruption and copy. */
        for (pgnum = 0; pgnum < PAGES_PER_CHUNK; pgnum++) {
                u32 offset = pgnum * PAGE_SIZE;
                phys_addr_t addr = bad_chunk_addr + offset;
                u32 *virt;
                u32 *recover_page = recover_chunk + offset / sizeof(u32);
 
+               if (should_skip_fn(addr)) {
+                       struct page *page = phys_to_page(addr);
+
+                       /*
+                        * If the page is unused then we really don't care that
+                        * we can't recover it.  Just continue on.
+                        */
+                       if (atomic_read(&page->_count) == 0) {
+                               pr_info("%s: Skip unused page at %08x\n",
+                                       __func__, addr);
+                               continue;
+                       }
+
+                       /*
+                        * If one page in a chunk has bit errors it's likely
+                        * that other pages in the chunk will have errors too.
+                        * Unfortunately we can't check, so reboot and be safe
+                        * rather than sorry.
+                        */
+                       pr_err("%s: Can't recover skipped page at %08x\n",
+                              __func__, addr);
+                       panic("Rebooting due to likely bit errors\n");
+               }
+
                virt = kmap_atomic(phys_to_page(addr));
                bytes_fixed += bitfix_compare(addr, virt, recover_page);
                memcpy(virt, recover_page, PAGE_SIZE);
index 240430f..4b5752f 100644 (file)
@@ -109,7 +109,7 @@ static bool s3c_pm_should_skip_page(phys_addr_t addr)
 {
        if (pm_check_skip_unused) {
                struct page *page = phys_to_page(addr);
-               if  (page->_count.counter == 0) {
+               if (atomic_read(&page->_count) == 0) {
                        s3c_pm_printskip("unused", addr);
                        return true;
                }