Re: [patch 5/8] [Intel IOMMU] Graphics driver workarounds to provide unity map

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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)

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux