Make __get_user_pages return -EHWPOISON for HWPOISON page only if
FOLL_HWPOISON is specified.  With this patch, the interested callers
can distinguish HWPOISON pages from general FAULT pages, while other
callers will still get -EFAULT for all these pages, so the user space
interface need not to be changed.
This feature is needed by KVM, where UCR MCE should be relayed to
guest for HWPOISON page, while instruction emulation and MMIO will be
tried for general FAULT page.
The idea comes from Andrew Morton.
Signed-off-by: Huang Ying <ying.huang@intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
 
 #define        ERFKILL         138     /* Operation not possible due to RF-kill */
 
+#define EHWPOISON      139     /* Memory page has hardware error */
+
 #endif
 
 
 #define        ERFKILL         167     /* Operation not possible due to RF-kill */
 
+#define EHWPOISON      168     /* Memory page has hardware error */
+
 #define EDQUOT         1133    /* Quota exceeded */
 
 #ifdef __KERNEL__
 
 
 #define        ERFKILL         256     /* Operation not possible due to RF-kill */
 
+#define EHWPOISON      257     /* Memory page has hardware error */
+
 #endif
 
 
 #define        ERFKILL         134     /* Operation not possible due to RF-kill */
 
+#define EHWPOISON      135     /* Memory page has hardware error */
+
 #endif
 
 
 #define ERFKILL                132     /* Operation not possible due to RF-kill */
 
+#define EHWPOISON      133     /* Memory page has hardware error */
+
 #endif
 
 #define FOLL_FORCE     0x10    /* get_user_pages read/write w/o permission */
 #define FOLL_MLOCK     0x40    /* mark page as mlocked */
 #define FOLL_SPLIT     0x80    /* don't return transhuge pages, split them */
+#define FOLL_HWPOISON  0x100   /* check page is hwpoisoned */
 
 typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr,
                        void *data);
 
                                if (ret & VM_FAULT_ERROR) {
                                        if (ret & VM_FAULT_OOM)
                                                return i ? i : -ENOMEM;
-                                       if (ret &
-                                           (VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE|
-                                            VM_FAULT_SIGBUS))
+                                       if (ret & (VM_FAULT_HWPOISON |
+                                                  VM_FAULT_HWPOISON_LARGE)) {
+                                               if (i)
+                                                       return i;
+                                               else if (gup_flags & FOLL_HWPOISON)
+                                                       return -EHWPOISON;
+                                               else
+                                                       return -EFAULT;
+                                       }
+                                       if (ret & VM_FAULT_SIGBUS)
                                                return i ? i : -EFAULT;
                                        BUG();
                                }