[PATCH] mm: Work around Intel SNB GTT bug with some physical pages.

From: StÃphane Marchesin
Date: Mon May 07 2012 - 19:14:08 EST


While investing some Sandy Bridge rendering corruption, I found out
that all physical memory pages below 1MiB were returning garbage when
read through the GTT. This has been causing graphics corruption (when
it's used for textures, render targets and pixmaps) and GPU hangups
(when it's used for GPU batch buffers).

I talked with some people at Intel and they confirmed my findings,
and said that a couple of other random pages were also affected.

We could fix this problem by adding an e820 region preventing the
memory below 1 MiB to be used, but that prevents at least my machine
from booting. One could think that we should be able to fix it in
i915, but since the allocation is done by the backing shmem this is
not possible.

In the end, I came up with the ugly workaround of just leaking the
offending pages in shmem.c. I do realize it's truly ugly, but I'm
looking for a fix to the existing code, and am wondering if people on
this list have a better idea, short of rewriting i915_gem.c to
allocate its own pages directly.

Signed-off-by: StÃphane Marchesin <marcheu@xxxxxxxxxxxx>

Change-Id: I957e125fb280e0b0d6b05a83cc4068df2f05aa0a
---
mm/shmem.c | 39 +++++++++++++++++++++++++++++++++++++--
1 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/mm/shmem.c b/mm/shmem.c
index 6c253f7..dcbb58b 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -768,6 +768,31 @@ redirty:
return 0;
}

+/*
+ * Some intel GPUs can't use those pages in the GTT, which results in
+ * graphics corruption. Sadly, it's impossible to prevent usage of those
+ * pages in the intel allocator.
+ *
+ * Instead, we test for those areas here and leak the corresponding pages.
+ *
+ * Some day, when the intel GPU memory is not backed by shmem any more,
+ * we'll be able to come up with a solution which is contained in i915.
+ */
+static bool i915_usable_page(struct page *page)
+{
+ dma_addr_t addr = page_to_phys(page);
+
+ if (unlikely((addr < 1 * 1024 * 1024) ||
+ (addr == 0x20050000) ||
+ (addr == 0x20110000) ||
+ (addr == 0x20130000) ||
+ (addr == 0x20138000) ||
+ (addr == 0x40004000)))
+ return false;
+
+ return true;
+}
+
#ifdef CONFIG_NUMA
#ifdef CONFIG_TMPFS
static void shmem_show_mpol(struct seq_file *seq, struct mempolicy *mpol)
@@ -816,6 +841,7 @@ static struct page *shmem_alloc_page(gfp_t gfp,
struct shmem_inode_info *info, pgoff_t index)
{
struct vm_area_struct pvma;
+ struct page *page;

/* Create a pseudo vma that just contains the policy */
pvma.vm_start = 0;
@@ -826,7 +852,11 @@ static struct page *shmem_alloc_page(gfp_t gfp,
/*
* alloc_page_vma() will drop the shared policy reference
*/
- return alloc_page_vma(gfp, &pvma, 0);
+ do {
+ page = alloc_page_vma(gfp, &pvma, 0);
+ } while (!i915_usable_page(page));
+
+ return page;
}
#else /* !CONFIG_NUMA */
#ifdef CONFIG_TMPFS
@@ -844,7 +874,12 @@ static inline struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp,
static inline struct page *shmem_alloc_page(gfp_t gfp,
struct shmem_inode_info *info, pgoff_t index)
{
- return alloc_page(gfp);
+ struct page *page;
+ do {
+ page = alloc_page(gfp);
+ } while (!i915_usable_page(page));
+
+ return page;
}
#endif /* CONFIG_NUMA */

--
1.7.5.3.367.ga9930

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/