Skip to content
Snippets Groups Projects
memory-failure.c 29.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	/*
    	 * Torn down by someone else?
    	 */
    
    	if (PageLRU(p) && !PageSwapCache(p) && p->mapping == NULL) {
    
    		action_result(pfn, "already truncated LRU", IGNORED);
    
    		goto out;
    	}
    
    	res = -EBUSY;
    	for (ps = error_states;; ps++) {
    
    		if ((p->flags & ps->mask) == ps->res) {
    
    			break;
    		}
    	}
    out:
    	unlock_page(p);
    	return res;
    }
    EXPORT_SYMBOL_GPL(__memory_failure);
    
    /**
     * memory_failure - Handle memory failure of a page.
     * @pfn: Page Number of the corrupted page
     * @trapno: Trap number reported in the signal to user space.
     *
     * This function is called by the low level machine check code
     * of an architecture when it detects hardware memory corruption
     * of a page. It tries its best to recover, which includes
     * dropping pages, killing processes etc.
     *
     * The function is primarily of use for corruptions that
     * happen outside the current execution context (e.g. when
     * detected by a background scrubber)
     *
     * Must run in process context (e.g. a work queue) with interrupts
     * enabled and no spinlocks hold.
     */
    void memory_failure(unsigned long pfn, int trapno)
    {
    	__memory_failure(pfn, trapno, 0);
    }
    
    
    /**
     * unpoison_memory - Unpoison a previously poisoned page
     * @pfn: Page number of the to be unpoisoned page
     *
     * Software-unpoison a page that has been poisoned by
     * memory_failure() earlier.
     *
     * This is only done on the software-level, so it only works
     * for linux injected failures, not real hardware failures
     *
     * Returns 0 for success, otherwise -errno.
     */
    int unpoison_memory(unsigned long pfn)
    {
    	struct page *page;
    	struct page *p;
    	int freeit = 0;
    
    	if (!pfn_valid(pfn))
    		return -ENXIO;
    
    	p = pfn_to_page(pfn);
    	page = compound_head(p);
    
    	if (!PageHWPoison(p)) {
    		pr_debug("MCE: Page was already unpoisoned %#lx\n", pfn);
    		return 0;
    	}
    
    	if (!get_page_unless_zero(page)) {
    		if (TestClearPageHWPoison(p))
    			atomic_long_dec(&mce_bad_pages);
    		pr_debug("MCE: Software-unpoisoned free page %#lx\n", pfn);
    		return 0;
    	}
    
    	lock_page_nosync(page);
    	/*
    	 * This test is racy because PG_hwpoison is set outside of page lock.
    	 * That's acceptable because that won't trigger kernel panic. Instead,
    	 * the PG_hwpoison page will be caught and isolated on the entrance to
    	 * the free buddy page pool.
    	 */
    	if (TestClearPageHWPoison(p)) {
    		pr_debug("MCE: Software-unpoisoned page %#lx\n", pfn);
    		atomic_long_dec(&mce_bad_pages);
    		freeit = 1;
    	}
    	unlock_page(page);
    
    	put_page(page);
    	if (freeit)
    		put_page(page);
    
    	return 0;
    }
    EXPORT_SYMBOL(unpoison_memory);