hugetlbfs: use i_mmap_rwsem for more pmd sharing synchronization
[linux-2.6-microblaze.git] / mm / memory-failure.c
index 41c634f..1c961cd 100644 (file)
@@ -954,7 +954,7 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn,
        enum ttu_flags ttu = TTU_IGNORE_MLOCK | TTU_IGNORE_ACCESS;
        struct address_space *mapping;
        LIST_HEAD(tokill);
-       bool unmap_success;
+       bool unmap_success = true;
        int kill = 1, forcekill;
        struct page *hpage = *hpagep;
        bool mlocked = PageMlocked(hpage);
@@ -1016,7 +1016,32 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn,
        if (kill)
                collect_procs(hpage, &tokill, flags & MF_ACTION_REQUIRED);
 
-       unmap_success = try_to_unmap(hpage, ttu);
+       if (!PageHuge(hpage)) {
+               unmap_success = try_to_unmap(hpage, ttu);
+       } else {
+               /*
+                * For hugetlb pages, try_to_unmap could potentially call
+                * huge_pmd_unshare.  Because of this, take semaphore in
+                * write mode here and set TTU_RMAP_LOCKED to indicate we
+                * have taken the lock at this higer level.
+                *
+                * Note that the call to hugetlb_page_mapping_lock_write
+                * is necessary even if mapping is already set.  It handles
+                * ugliness of potentially having to drop page lock to obtain
+                * i_mmap_rwsem.
+                */
+               mapping = hugetlb_page_mapping_lock_write(hpage);
+
+               if (mapping) {
+                       unmap_success = try_to_unmap(hpage,
+                                                    ttu|TTU_RMAP_LOCKED);
+                       i_mmap_unlock_write(mapping);
+               } else {
+                       pr_info("Memory failure: %#lx: could not find mapping for mapped huge page\n",
+                               pfn);
+                       unmap_success = false;
+               }
+       }
        if (!unmap_success)
                pr_err("Memory failure: %#lx: failed to unmap page (mapcount=%d)\n",
                       pfn, page_mapcount(hpage));