Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc2a1cbf authored by Wu Fengguang's avatar Wu Fengguang Committed by Andi Kleen
Browse files

HWPOISON: introduce delete_from_lru_cache()



Introduce delete_from_lru_cache() to
- clear PG_active, PG_unevictable to avoid complains at unpoison time
- move the isolate_lru_page() call back to the handlers instead of the
  entrance of __memory_failure(), this is more hwpoison filter friendly

Signed-off-by: default avatarWu Fengguang <fengguang.wu@intel.com>
Signed-off-by: default avatarAndi Kleen <ak@linux.intel.com>
parent 71f72525
Loading
Loading
Loading
Loading
+37 −8
Original line number Original line Diff line number Diff line
@@ -349,6 +349,30 @@ static const char *action_name[] = {
	[RECOVERED] = "Recovered",
	[RECOVERED] = "Recovered",
};
};


/*
 * XXX: It is possible that a page is isolated from LRU cache,
 * and then kept in swap cache or failed to remove from page cache.
 * The page count will stop it from being freed by unpoison.
 * Stress tests should be aware of this memory leak problem.
 */
static int delete_from_lru_cache(struct page *p)
{
	if (!isolate_lru_page(p)) {
		/*
		 * Clear sensible page flags, so that the buddy system won't
		 * complain when the page is unpoison-and-freed.
		 */
		ClearPageActive(p);
		ClearPageUnevictable(p);
		/*
		 * drop the page count elevated by isolate_lru_page()
		 */
		page_cache_release(p);
		return 0;
	}
	return -EIO;
}

/*
/*
 * Error hit kernel page.
 * Error hit kernel page.
 * Do nothing, try to be lucky and not touch this instead. For a few cases we
 * Do nothing, try to be lucky and not touch this instead. For a few cases we
@@ -393,6 +417,8 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn)
	int ret = FAILED;
	int ret = FAILED;
	struct address_space *mapping;
	struct address_space *mapping;


	delete_from_lru_cache(p);

	/*
	/*
	 * For anonymous pages we're done the only reference left
	 * For anonymous pages we're done the only reference left
	 * should be the one m_f() holds.
	 * should be the one m_f() holds.
@@ -522,14 +548,20 @@ static int me_swapcache_dirty(struct page *p, unsigned long pfn)
	/* Trigger EIO in shmem: */
	/* Trigger EIO in shmem: */
	ClearPageUptodate(p);
	ClearPageUptodate(p);


	if (!delete_from_lru_cache(p))
		return DELAYED;
		return DELAYED;
	else
		return FAILED;
}
}


static int me_swapcache_clean(struct page *p, unsigned long pfn)
static int me_swapcache_clean(struct page *p, unsigned long pfn)
{
{
	delete_from_swap_cache(p);
	delete_from_swap_cache(p);


	if (!delete_from_lru_cache(p))
		return RECOVERED;
		return RECOVERED;
	else
		return FAILED;
}
}


/*
/*
@@ -746,7 +778,6 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn,


int __memory_failure(unsigned long pfn, int trapno, int flags)
int __memory_failure(unsigned long pfn, int trapno, int flags)
{
{
	unsigned long lru_flag;
	struct page_state *ps;
	struct page_state *ps;
	struct page *p;
	struct page *p;
	int res;
	int res;
@@ -796,13 +827,11 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
	 */
	 */
	if (!PageLRU(p))
	if (!PageLRU(p))
		lru_add_drain_all();
		lru_add_drain_all();
	lru_flag = p->flags & lru;
	if (!PageLRU(p)) {
	if (isolate_lru_page(p)) {
		action_result(pfn, "non LRU", IGNORED);
		action_result(pfn, "non LRU", IGNORED);
		put_page(p);
		put_page(p);
		return -EBUSY;
		return -EBUSY;
	}
	}
	page_cache_release(p);


	/*
	/*
	 * Lock the page and wait for writeback to finish.
	 * Lock the page and wait for writeback to finish.
@@ -825,7 +854,7 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
	/*
	/*
	 * Torn down by someone else?
	 * Torn down by someone else?
	 */
	 */
	if ((lru_flag & lru) && !PageSwapCache(p) && p->mapping == NULL) {
	if (PageLRU(p) && !PageSwapCache(p) && p->mapping == NULL) {
		action_result(pfn, "already truncated LRU", IGNORED);
		action_result(pfn, "already truncated LRU", IGNORED);
		res = 0;
		res = 0;
		goto out;
		goto out;
@@ -833,7 +862,7 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)


	res = -EBUSY;
	res = -EBUSY;
	for (ps = error_states;; ps++) {
	for (ps = error_states;; ps++) {
		if (((p->flags | lru_flag)& ps->mask) == ps->res) {
		if ((p->flags & ps->mask) == ps->res) {
			res = page_action(ps, p, pfn);
			res = page_action(ps, p, pfn);
			break;
			break;
		}
		}