map user space memory as gart memory for intel integrated graphics chip

Austin Yuan yuanshengquan at gmail.com
Thu Dec 8 03:52:17 PST 2005


Just an idea when thinking about the issue on
http://lists.freedesktop.org/pipermail/xorg/2005-April/007662.html.
I have done some experiment on it, and the idea here is doable, but
the method may be ugly and a little tricky.

For Intel integrated graphics chips, gart driver programs system memory
address into gart table, then graphics engine can regard it as frame
buffer (just like the idea of AGP). In general, system memory is
allocated from kernel space. But for X window, 2D/3D driver are
running under user space, and X server/dri client always need to blt
between system memory and frame buffer (e.g. for X server, pixmap and
frame buffer bitblt, and for dri client, texture uploading). If we can
get the physical address of pixmap/texture memory, and program it into
gart table, then pixmap/texture memory can be used as frame buffer,
and we can use simple hw blt instructions to transfer image data
between pixmap/texture and frame buffer.

Below is a rough patch for Intel gart driver intel-agp.c, it receives
a user space address and then programs gart table to map it as frame
buffer. Because the interface of "alloc_by_type" only receives a
simple parameter "type", here I hide the user space address into
"type" and re-get it in alloc_userspace_memory.

I use this interface to easily implement XAA readpixmap/imagewrite
driver interfaces, and get a better performance. Here, I didn't attach
the patch for i810 driver. I just want to get some comments about it,
and if you think it makes sense, I'd like to make it more generic.

Any comments are appreciated, thanks.

Austin


diff -Nur agp-orig/intel-agp.c agp/intel-agp.c
--- agp-orig/intel-agp.c        2005-12-08 18:32:48.000000000 +0800
+++ agp/intel-agp.c     2005-12-08 19:08:17.000000000 +0800
@@ -59,11 +59,13 @@

#define AGP_DCACHE_MEMORY      1
#define AGP_PHYS_MEMORY                2
+#define AGP_USERSPACE_MEMORY    3

static struct gatt_mask intel_i810_masks[] =
{
       {.mask = I810_PTE_VALID, .type = 0},
       {.mask = (I810_PTE_VALID | I810_PTE_LOCAL), .type = AGP_DCACHE_MEMORY},
+       {.mask = I810_PTE_VALID, .type = 0},
       {.mask = I810_PTE_VALID, .type = 0}
};

@@ -300,6 +302,57 @@
       return new;
}

+
+/*
+ * hack for i8xx for bitblt from fb to system memory
+ * map userspace memory into gart memory
+ */
+#define ENTRIES_PER_PAGE       (PAGE_SIZE / sizeof(unsigned long))
+
+static struct agp_memory *alloc_userspace_memory(size_t pg_count, int type)
+{
+    struct agp_memory *new;
+    unsigned long uaddr;
+    int result,scratch_pages,i;
+    struct page **pages;
+
+    /* a hack to get uaddr from type */
+    uaddr = ((unsigned long)type) & PAGE_MASK;
+    pages = kmalloc(pg_count * sizeof(struct page *), GFP_KERNEL);
+    if (pages == NULL)
+        return NULL;
+    type = AGP_USERSPACE_MEMORY; /* normal value */
+
+    down_read(&current->mm->mmap_sem);
+    result = get_user_pages(current,current->mm,uaddr,pg_count,
+                            1,0 /* not force */,pages,NULL);
+    up_read(&current->mm->mmap_sem);
+    if (result < pg_count) { /* cann't map all pages */
+        kfree(pages);
+        return NULL;
+    }
+
+    scratch_pages = (pg_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
+    new = agp_create_memory(scratch_pages);
+    if (new == NULL) {
+        kfree(pages);
+        return NULL;
+    }
+
+    new->type = AGP_USERSPACE_MEMORY;
+    new->pages = pages;
+    for (i = 0; i < pg_count; i++) {
+        void *addr = kmap(pages[i]);
+
+        new->memory[i] =
+            agp_bridge->driver->mask_memory(agp_bridge,virt_to_phys(addr),
type);
+        new->page_count++;
+    }
+
+    return new;
+}
+
+
static struct agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type)
{
       struct agp_memory *new;
@@ -320,6 +373,9 @@
       }
       if (type == AGP_PHYS_MEMORY)
               return alloc_agpphysmem_i8xx(pg_count, type);
+
+        if ((type & (PAGE_SIZE - 1)) == AGP_USERSPACE_MEMORY)
+            return(alloc_userspace_memory(pg_count,type));

       return NULL;
}
@@ -334,7 +390,21 @@
                       agp_bridge->driver->agp_destroy_page(
                                gart_to_virt(curr->memory[0]));
               vfree(curr->memory);
-       }
+       } else if (curr->type == AGP_USERSPACE_MEMORY) {
+                int i;
+                vfree(curr->memory);/* scratch pages */
+                if (curr->pages) {
+                    for (i=0;i < curr->page_count; i++) {
+                        if (!PageReserved(curr->pages[i]))
+                            SetPageDirty(curr->pages[i]);
+                        page_cache_release(curr->pages[i]);
+                    }
+                    kfree(curr->pages);
+                    curr->pages = NULL;
+                }
+        }
+
+
       kfree(curr);
}

@@ -578,9 +648,10 @@
       /* The i830 can't check the GTT for entries since its read only,
        * depend on the caller to make the correct offset decisions.
        */
-
-       if ((type != 0 && type != AGP_PHYS_MEMORY) ||
-               (mem->type != 0 && mem->type != AGP_PHYS_MEMORY))
+       if (((type != 0 && type != AGP_PHYS_MEMORY) ||
+             (mem->type != 0 && mem->type != AGP_PHYS_MEMORY)) &&
+            ((type != 0 && type != AGP_USERSPACE_MEMORY) ||
+             (mem->type != 0 && mem->type != AGP_USERSPACE_MEMORY)))
               return -EINVAL;

       global_cache_flush();   /* FIXME: Necessary ?*/



More information about the xorg mailing list