diff options
Diffstat (limited to 'virt/kvm/kvm_main.c')
-rw-r--r-- | virt/kvm/kvm_main.c | 32 |
1 files changed, 30 insertions, 2 deletions
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 7f686251f711..85ab7db0d366 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
@@ -104,8 +104,36 @@ static pfn_t fault_pfn; | |||
104 | inline int kvm_is_mmio_pfn(pfn_t pfn) | 104 | inline int kvm_is_mmio_pfn(pfn_t pfn) |
105 | { | 105 | { |
106 | if (pfn_valid(pfn)) { | 106 | if (pfn_valid(pfn)) { |
107 | struct page *page = compound_head(pfn_to_page(pfn)); | 107 | struct page *head; |
108 | return PageReserved(page); | 108 | struct page *tail = pfn_to_page(pfn); |
109 | head = compound_head(tail); | ||
110 | if (head != tail) { | ||
111 | smp_rmb(); | ||
112 | /* | ||
113 | * head may be a dangling pointer. | ||
114 | * __split_huge_page_refcount clears PageTail | ||
115 | * before overwriting first_page, so if | ||
116 | * PageTail is still there it means the head | ||
117 | * pointer isn't dangling. | ||
118 | */ | ||
119 | if (PageTail(tail)) { | ||
120 | /* | ||
121 | * the "head" is not a dangling | ||
122 | * pointer but the hugepage may have | ||
123 | * been splitted from under us (and we | ||
124 | * may not hold a reference count on | ||
125 | * the head page so it can be reused | ||
126 | * before we run PageReferenced), so | ||
127 | * we've to recheck PageTail before | ||
128 | * returning what we just read. | ||
129 | */ | ||
130 | int reserved = PageReserved(head); | ||
131 | smp_rmb(); | ||
132 | if (PageTail(tail)) | ||
133 | return reserved; | ||
134 | } | ||
135 | } | ||
136 | return PageReserved(tail); | ||
109 | } | 137 | } |
110 | 138 | ||
111 | return true; | 139 | return true; |