lock_page(head);
        page_flags = head->flags;
 
-       if (!PageHWPoison(head)) {
-               pr_err("Memory failure: %#lx: just unpoisoned\n", pfn);
-               num_poisoned_pages_dec();
-               unlock_page(head);
-               put_page(head);
-               return 0;
-       }
-
        /*
         * TODO: hwpoison for pud-sized hugetlb doesn't work right now, so
         * simply disable it. In order to make it work properly, we need
        return rc;
 }
 
+static DEFINE_MUTEX(mf_mutex);
+
 /**
  * memory_failure - Handle memory failure of a page.
  * @pfn: Page Number of the corrupted page
        int res = 0;
        unsigned long page_flags;
        bool retry = true;
-       static DEFINE_MUTEX(mf_mutex);
 
        if (!sysctl_memory_failure_recovery)
                panic("Memory failure on page %lx", pfn);
         */
        page_flags = p->flags;
 
-       /*
-        * unpoison always clear PG_hwpoison inside page lock
-        */
-       if (!PageHWPoison(p)) {
-               pr_err("Memory failure: %#lx: just unpoisoned\n", pfn);
-               num_poisoned_pages_dec();
-               unlock_page(p);
-               put_page(p);
-               goto unlock_mutex;
-       }
        if (hwpoison_filter(p)) {
                if (TestClearPageHWPoison(p))
                        num_poisoned_pages_dec();
        struct page *page;
        struct page *p;
        int freeit = 0;
+       int ret = 0;
        unsigned long flags = 0;
        static DEFINE_RATELIMIT_STATE(unpoison_rs, DEFAULT_RATELIMIT_INTERVAL,
                                        DEFAULT_RATELIMIT_BURST);
        p = pfn_to_page(pfn);
        page = compound_head(p);
 
+       mutex_lock(&mf_mutex);
+
        if (!PageHWPoison(p)) {
                unpoison_pr_info("Unpoison: Page was already unpoisoned %#lx\n",
                                 pfn, &unpoison_rs);
-               return 0;
+               goto unlock_mutex;
        }
 
        if (page_count(page) > 1) {
                unpoison_pr_info("Unpoison: Someone grabs the hwpoison page %#lx\n",
                                 pfn, &unpoison_rs);
-               return 0;
+               goto unlock_mutex;
        }
 
        if (page_mapped(page)) {
                unpoison_pr_info("Unpoison: Someone maps the hwpoison page %#lx\n",
                                 pfn, &unpoison_rs);
-               return 0;
+               goto unlock_mutex;
        }
 
        if (page_mapping(page)) {
                unpoison_pr_info("Unpoison: the hwpoison page has non-NULL mapping %#lx\n",
                                 pfn, &unpoison_rs);
-               return 0;
-       }
-
-       /*
-        * unpoison_memory() can encounter thp only when the thp is being
-        * worked by memory_failure() and the page lock is not held yet.
-        * In such case, we yield to memory_failure() and make unpoison fail.
-        */
-       if (!PageHuge(page) && PageTransHuge(page)) {
-               unpoison_pr_info("Unpoison: Memory failure is now running on %#lx\n",
-                                pfn, &unpoison_rs);
-               return 0;
+               goto unlock_mutex;
        }
 
        if (!get_hwpoison_page(p, flags)) {
                        num_poisoned_pages_dec();
                unpoison_pr_info("Unpoison: Software-unpoisoned free page %#lx\n",
                                 pfn, &unpoison_rs);
-               return 0;
+               goto unlock_mutex;
        }
 
-       lock_page(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(page)) {
                unpoison_pr_info("Unpoison: Software-unpoisoned page %#lx\n",
                                 pfn, &unpoison_rs);
                num_poisoned_pages_dec();
                freeit = 1;
        }
-       unlock_page(page);
 
        put_page(page);
        if (freeit && !(pfn == my_zero_pfn(0) && page_count(p) == 1))
                put_page(page);
 
-       return 0;
+unlock_mutex:
+       mutex_unlock(&mf_mutex);
+       return ret;
 }
 EXPORT_SYMBOL(unpoison_memory);
 
                return -EIO;
        }
 
+       mutex_lock(&mf_mutex);
+
        if (PageHWPoison(page)) {
                pr_info("%s: %#lx page already poisoned\n", __func__, pfn);
                put_ref_page(ref_page);
+               mutex_unlock(&mf_mutex);
                return 0;
        }
 
                }
        }
 
+       mutex_unlock(&mf_mutex);
+
        return ret;
 }