On 2007.04.10 11:12:17 +0000, Andi Kleen wrote:
> > > On Mon, Apr 09, 2007 at 02:55:57PM -0700, Ashok Raj wrote:
> > > > Most GFX drivers don't call standard PCI DMA APIs to allocate DMA 
> > > > buffer,
> > > > Such drivers will be broken with IOMMU enabled. To workaround this 
> > > > issue, 
> > > > we added two options.
> > > 
> > > All drm drivers do it.  If the usual out of tree crap vendors are too
> > > stupid for their own sake it's their fault.
> > > 
> > > So NACK to this patch.
> > 
> > That's my feeling as well, everything we care about should be using
> > the proper APIs or else what is the point of them...
> 
> They can't. There is no proper API to do IOMMU mappings from user space.
> And that is how Xorg on x86 works.
> 
> I had some hackish patches to enable mapping on 
> /sys/bus/pci/.../coherent_mem, but
> you need ioctls to pass out the translated address and it wasn't
> exactly pretty. Still also not sure that's the right way.
> 

For agpgart based gfx driver, we need to enable dma mapping on agpgart module, 
which would work in the IOMMU case. I attach my current patches to do this.
Dave, how do you think about it?

diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h
index 552815d..0d3312d 100644
--- a/drivers/char/agp/agp.h
+++ b/drivers/char/agp/agp.h
@@ -115,6 +115,9 @@ struct agp_bridge_driver {
        void *(*agp_alloc_page)(struct agp_bridge_data *);
        void (*agp_destroy_page)(void *);
         int (*agp_type_to_mask_type) (struct agp_bridge_data *, int);
+       int (*agp_map_dma_pages)(struct agp_bridge_data *, struct agp_memory*);
+       void (*agp_unmap_dma_pages)(struct agp_bridge_data *, 
+                       struct agp_memory *);
 };
 
 struct agp_bridge_data {
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index f902d71..03a001a 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -112,22 +112,34 @@ void agp_alloc_page_array(size_t size, s
        mem->memory = NULL;
        mem->vmalloc_flag = 0;
 
-       if (size <= 2*PAGE_SIZE)
+       if (size <= 2*PAGE_SIZE) {
                mem->memory = kmalloc(size, GFP_KERNEL | __GFP_NORETRY);
+               mem->dma_memory = kmalloc(size, GFP_KERNEL | __GFP_NORETRY);
+       }
        if (mem->memory == NULL) {
                mem->memory = vmalloc(size);
-               mem->vmalloc_flag = 1;
+               mem->vmalloc_flag = AGP_MEMORY_VMALLOC;
+       }
+       if (mem->dma_memory == NULL) {
+               mem->dma_memory = vmalloc(size);
+               /* XXX this is a little overdo */
+               mem->vmalloc_flag |= AGP_DMA_MEMORY_VMALLOC;
        }
 }
 EXPORT_SYMBOL(agp_alloc_page_array);
 
 void agp_free_page_array(struct agp_memory *mem)
 {
-       if (mem->vmalloc_flag) {
+       if (mem->vmalloc_flag & AGP_MEMORY_VMALLOC) {
                vfree(mem->memory);
        } else {
                kfree(mem->memory);
        }
+       if (mem->vmalloc_flag & AGP_DMA_MEMORY_VMALLOC) {
+               vfree(mem->dma_memory);
+       } else {
+               kfree(mem->dma_memory);
+       }
 }
 EXPORT_SYMBOL(agp_free_page_array);
 
@@ -155,6 +167,15 @@ static struct agp_memory *agp_create_use
                kfree(new);
                return NULL;
        }
+       if (new->dma_memory == NULL) {
+               agp_free_key(new->key);
+               if (new->vmalloc_flag)
+                       vfree(new->memory);
+               else
+                       kfree(new->memory);
+               kfree(new);
+               return NULL;
+       }
        new->num_scratch_pages = 0;
        return new;
 }
@@ -181,6 +202,15 @@ struct agp_memory *agp_create_memory(int
                kfree(new);
                return NULL;
        }
+       if (new->dma_memory == NULL) {
+               agp_free_key(new->key);
+               if (new->vmalloc_flag)
+                       vfree(new->memory);
+               else
+                       kfree(new->memory);
+               kfree(new);
+               return NULL;
+       }
        new->num_scratch_pages = scratch_pages;
        new->type = AGP_NORMAL_MEMORY;
        return new;
@@ -433,6 +463,13 @@ int agp_bind_memory(struct agp_memory *c
                curr->bridge->driver->cache_flush();
                curr->is_flushed = TRUE;
        }
+
+       if (curr->bridge->driver->agp_map_dma_pages) {
+               if (!curr->bridge->driver->agp_map_dma_pages(curr->bridge, 
curr))
+                       return -EINVAL;
+               curr->is_dma_mapped = 1;
+       }
+
        ret_val = curr->bridge->driver->insert_memory(curr, pg_start, 
curr->type);
 
        if (ret_val != 0)
@@ -470,6 +507,10 @@ int agp_unbind_memory(struct agp_memory
        if (ret_val != 0)
                return ret_val;
 
+       if (curr->bridge->driver->agp_unmap_dma_pages)
+               curr->bridge->driver->agp_unmap_dma_pages(curr->bridge, curr);
+       curr->is_dma_mapped = 0;
+
        curr->is_bound = FALSE;
        curr->pg_start = 0;
        return 0;
@@ -1094,8 +1135,13 @@ int agp_generic_insert_memory(struct agp
        }
 
        for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
-               writel(bridge->driver->mask_memory(bridge, mem->memory[i], 
mask_type),
-                      bridge->gatt_table+j);
+               if (mem->is_dma_mapped)
+                       writel(bridge->driver->mask_memory(bridge, 
+                                               mem->dma_memory[i], mask_type),
+                                       bridge->gatt_table+j);
+               else
+                       writel(bridge->driver->mask_memory(bridge, 
mem->memory[i], mask_type),
+                                       bridge->gatt_table+j);
        }
        readl(bridge->gatt_table+j-1);  /* PCI Posting. */
 
@@ -1356,3 +1406,41 @@ const struct aper_size_info_16 agp3_gene
 };
 EXPORT_SYMBOL(agp3_generic_sizes);
 
+int agp_generic_map_dma_pages(struct agp_bridge_data *bridge, struct 
agp_memory *mem)
+{
+       struct pci_dev *dev;
+       int i;
+
+       if (!bridge || !mem)
+               return 0;
+       dev = bridge->dev;
+       for (i = 0; i < mem->page_count; i++ ) {
+               mem->dma_memory[i] = pci_map_single(dev, 
+                               gart_to_virt(mem->memory[i]), PAGE_SIZE, 
+                               PCI_DMA_BIDIRECTIONAL);
+               if (pci_dma_mapping_error(mem->dma_memory[i])) {
+                       int j;
+                       for (j = 0; j < i; j++) {
+                               pci_unmap_single(dev, mem->dma_memory[j],
+                                               PAGE_SIZE, 
+                                               PCI_DMA_BIDIRECTIONAL);
+                       }
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+void agp_generic_unmap_dma_pages(struct agp_bridge_data *bridge, struct 
agp_memory *mem)
+{
+       struct pci_dev *dev;
+       int i;
+       if (!bridge || !mem)
+               return;
+       dev = bridge->dev;
+       for (i = 0; i < mem->page_count; i++ )
+               pci_unmap_single(dev, mem->dma_memory[i], PAGE_SIZE,
+                               PCI_DMA_BIDIRECTIONAL);
+}
+EXPORT_SYMBOL(agp_generic_map_dma_pages);
+EXPORT_SYMBOL(agp_generic_unmap_dma_pages);
diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h
index abc521c..abd2ca6 100644
--- a/include/linux/agp_backend.h
+++ b/include/linux/agp_backend.h
@@ -79,6 +79,8 @@ struct agp_memory {
        struct agp_memory *prev;
        struct agp_bridge_data *bridge;
        unsigned long *memory;
+       dma_addr_t *dma_memory;
+       u8 is_dma_mapped;
        size_t page_count;
        int key;
        int num_scratch_pages;
@@ -90,6 +92,9 @@ struct agp_memory {
         u8 vmalloc_flag;
 };
 
+#define AGP_MEMORY_VMALLOC     1
+#define AGP_DMA_MEMORY_VMALLOC  2
+
 #define AGP_NORMAL_MEMORY 0
 
 #define AGP_USER_TYPES (1 << 16)

Reply via email to