Attached is a set of RFC patches that make it possible for AGP graphic drivers to
work under Xen. The major problem that Linux kernel has when running under Xen
is that the usage of "virt_to_phys(x) >> PAGE_SIZE" to get the DMA address is not
applicable. That is due to the fact that the PFN value is not the real Machine
Frame Number (MFN), hence virt_to_phys(x) >> PAGE_SIZE ends up pointing to a
random physical address. But if you use the PCI API, then the DMA (bus) address
returned is a valid MFN.
This macro (virt_to_phys) is used throughout the AGP backends when either constructing
the GART or when stitching pages in the GART tables and programming the GATT.
One way to fix this is to provide a lookup (similar to how the internal Xen MMU
does it) in the AGP backends to take the PFN (or phys) and find the "real" MFN,
such as this skanky code:
- addr_t phys = page_to_phys(mem->pages[i]);
+ if (xen_pv_domain()) {
+ phys_addr_t xen_phys = PFN_PHYS(pfn_to_mfn(
+ page_to_pfn(mem->pages[i])));
+ if (phys != xen_phys) {
+ printk(KERN_ERR "Fixing up GART: (0x%lx->0x%lx)." \
+ " CODE UNTESTED!\n",
+ (unsigned long)phys,
+ (unsigned long)xen_phys);
+ WARN_ON_ONCE(phys != xen_phys);
+ phys = xen_phys;
+ }
+ }
tmp = agp_bridge->driver->mask_memory(agp_bridge,
- page_to_phys(mem->pages[i]),
+ phys,
mask_type);
Truly horrible. The other way that I"ve pursued is to pass the responsibility
of providing the DMA address to the callers of the AGP API and using the PCI API
to get the real DMA address. In the past the big user of AGPI API used to be the
XServer, but now that is taken over by the TTM and the DRM API so everything is
nicely contained within the kernel.
This set of patches attached does three major things for the X86 platform:
1). If graphic driver uses the agp_generic_create_gatt_table to set up the GART
make the allocation for the GART use the pci_alloc_consistent so that the
bus address can be programmed to the GATT instead of using virt_to_phys macro.
2). When using agp_alloc_page(s), a new argument would be added which would have
an array of dma_addr_t to be populated. Some of the callers of this
function use the AGP_USER_TYPES type so the the responsibility
of populating the dma_addr_t falls to the callers (TTM for example).
3). When using the agp_bind_memory, the AGP PCI would use the dma_addr_t that would
have been populated by the agp_alloc_page(s) (or provided by the caller), to
program the GATT instead of using the page_to_phys(x) macro.
Basically extending the AGP API to pass the dma_addr around.
There are two extra set of patch series that demonstrate how the TTM and DRM layers
would have to be modified to utilize this.
Regressions tests have show no failures. I tested it on an ASUS P5PE (AGP), Dell
Optiplex 780 (Q45), Dell T105 (AMD, with Radeon 3450), BIOSTAR-MCP6P-M2 (Nvidia 6150).
But I haven't done any extensive testing on the older platforms yet as I was thinking
to get your guys idea before spending more resources on this.
This patch-set is also availble at:
git://git.kernel.org/pub/scm/linux/kernel/git/konrad/xen.git devel/agp.pci-api
The users of these changes are available in two other branches: devel/drm.pci-api and
devel/ttm.pci-api, which I will post soon too.
arch/alpha/include/asm/agp.h | 20 +++++++++--
arch/ia64/include/asm/agp.h | 32 +++++++++++++++--
arch/ia64/include/asm/dma-mapping.h | 2 +
arch/ia64/include/asm/sn/io.h | 16 ++++----
arch/parisc/include/asm/agp.h | 21 +++++++++--
arch/powerpc/include/asm/agp.h | 21 +++++++++--
arch/sparc/include/asm/agp.h | 20 +++++++++--
arch/x86/include/asm/agp.h | 32 +++++++++++++++--
drivers/char/agp/agp.h | 9 +++--
drivers/char/agp/ali-agp.c | 20 ++++++-----
drivers/char/agp/amd-k7-agp.c | 20 +++++++---
drivers/char/agp/amd64-agp.c | 4 +-
drivers/char/agp/ati-agp.c | 18 ++++++---
drivers/char/agp/backend.c | 17 ++++++---
drivers/char/agp/generic.c | 66 ++++++++++++++++++++++-------------
drivers/char/agp/hp-agp.c | 3 +-
drivers/char/agp/i460-agp.c | 7 ++--
drivers/char/agp/intel-gtt.c | 63 ++++++++++++++++++++++++---------
drivers/char/agp/nvidia-agp.c | 2 +-
drivers/char/agp/sgi-agp.c | 21 +----------
drivers/char/agp/sworks-agp.c | 18 ++++++---
include/linux/agp_backend.h | 4 ++-
22 files changed, 303 insertions(+), 133 deletions(-)
From: Konrad Rzeszutek Wilk <[email protected]>
This change is purely synthetic. We are adding in an extra
argument to agp_generic_[alloc|destroy] which is the dma_addr value.
We also update all drivers to be compile error-free.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/agp.h | 8 ++++----
drivers/char/agp/ali-agp.c | 16 +++++++++-------
drivers/char/agp/backend.c | 17 +++++++++++------
drivers/char/agp/generic.c | 17 ++++++++++-------
drivers/char/agp/intel-gtt.c | 9 ++++++---
drivers/char/agp/sgi-agp.c | 3 ++-
6 files changed, 42 insertions(+), 28 deletions(-)
diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h
index 5259065..6c60d68 100644
--- a/drivers/char/agp/agp.h
+++ b/drivers/char/agp/agp.h
@@ -115,9 +115,9 @@ struct agp_bridge_driver {
int (*remove_memory)(struct agp_memory *, off_t, int);
struct agp_memory *(*alloc_by_type) (size_t, int);
void (*free_by_type)(struct agp_memory *);
- struct page *(*agp_alloc_page)(struct agp_bridge_data *);
+ struct page *(*agp_alloc_page)(struct agp_bridge_data *, dma_addr_t *dma_addr);
int (*agp_alloc_pages)(struct agp_bridge_data *, struct agp_memory *, size_t);
- void (*agp_destroy_page)(struct page *, int flags);
+ void (*agp_destroy_page)(struct page *, int flags, dma_addr_t *dma_addr);
void (*agp_destroy_pages)(struct agp_memory *);
int (*agp_type_to_mask_type) (struct agp_bridge_data *, int);
void (*chipset_flush)(struct agp_bridge_data *);
@@ -199,10 +199,10 @@ int agp_generic_insert_memory(struct agp_memory *mem, off_t pg_start, int type);
int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type);
struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type);
void agp_generic_free_by_type(struct agp_memory *curr);
-struct page *agp_generic_alloc_page(struct agp_bridge_data *bridge);
+struct page *agp_generic_alloc_page(struct agp_bridge_data *bridge, dma_addr_t *dma_addr);
int agp_generic_alloc_pages(struct agp_bridge_data *agp_bridge,
struct agp_memory *memory, size_t page_count);
-void agp_generic_destroy_page(struct page *page, int flags);
+void agp_generic_destroy_page(struct page *page, int flags, dma_addr_t *dma_addr);
void agp_generic_destroy_pages(struct agp_memory *memory);
void agp_free_key(int key);
int agp_num_entries(void);
diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c
index fd79351..ede9d0a 100644
--- a/drivers/char/agp/ali-agp.c
+++ b/drivers/char/agp/ali-agp.c
@@ -141,9 +141,10 @@ static void m1541_cache_flush(void)
}
}
-static struct page *m1541_alloc_page(struct agp_bridge_data *bridge)
+static struct page *m1541_alloc_page(struct agp_bridge_data *bridge,
+ dma_addr_t *dma_addr)
{
- struct page *page = agp_generic_alloc_page(agp_bridge);
+ struct page *page = agp_generic_alloc_page(agp_bridge, dma_addr);
u32 temp;
if (!page)
@@ -156,18 +157,19 @@ static struct page *m1541_alloc_page(struct agp_bridge_data *bridge)
return page;
}
-static void ali_destroy_page(struct page *page, int flags)
+static void ali_destroy_page(struct page *page, int flags, dma_addr_t *dma_addr)
{
if (page) {
if (flags & AGP_PAGE_DESTROY_UNMAP) {
global_cache_flush(); /* is this really needed? --hch */
- agp_generic_destroy_page(page, flags);
+ agp_generic_destroy_page(page, flags, dma_addr);
} else
- agp_generic_destroy_page(page, flags);
+ agp_generic_destroy_page(page, flags, dma_addr);
}
}
-static void m1541_destroy_page(struct page *page, int flags)
+static void m1541_destroy_page(struct page *page, int flags,
+ dma_addr_t *dma_addr)
{
u32 temp;
@@ -182,7 +184,7 @@ static void m1541_destroy_page(struct page *page, int flags)
(((temp & ALI_CACHE_FLUSH_ADDR_MASK) |
page_to_phys(page)) | ALI_CACHE_FLUSH_EN));
}
- agp_generic_destroy_page(page, flags);
+ agp_generic_destroy_page(page, flags, dma_addr);
}
diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c
index f27d0d0..d119e89 100644
--- a/drivers/char/agp/backend.c
+++ b/drivers/char/agp/backend.c
@@ -142,7 +142,9 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge)
bridge->version = &agp_current_version;
if (bridge->driver->needs_scratch_page) {
- struct page *page = bridge->driver->agp_alloc_page(bridge);
+ struct page *page;
+ page = bridge->driver->agp_alloc_page(bridge,
+ &bridge->scratch_page_dma);
if (!page) {
dev_err(&bridge->dev->dev,
@@ -151,7 +153,6 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge)
}
bridge->scratch_page_page = page;
- bridge->scratch_page_dma = page_to_phys(page);
bridge->scratch_page = bridge->driver->mask_memory(bridge,
bridge->scratch_page_dma, 0);
@@ -197,8 +198,10 @@ err_out:
if (bridge->driver->needs_scratch_page) {
void *va = page_address(bridge->scratch_page_page);
- bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP);
- bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE);
+ bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP,
+ &bridge->scratch_page_dma);
+ bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE,
+ &bridge->scratch_page_dma);
}
if (got_gatt)
bridge->driver->free_gatt_table(bridge);
@@ -224,8 +227,10 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge)
bridge->driver->needs_scratch_page) {
void *va = page_address(bridge->scratch_page_page);
- bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP);
- bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE);
+ bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP,
+ &bridge->scratch_page_dma);
+ bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE,
+ &bridge->scratch_page_dma);
}
}
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index cf465b5..65fd7c7 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -212,12 +212,14 @@ void agp_free_memory(struct agp_memory *curr)
for (i = 0; i < curr->page_count; i++) {
curr->bridge->driver->agp_destroy_page(
curr->pages[i],
- AGP_PAGE_DESTROY_UNMAP);
+ AGP_PAGE_DESTROY_UNMAP,
+ &curr->dma_addr[i]);
}
for (i = 0; i < curr->page_count; i++) {
curr->bridge->driver->agp_destroy_page(
curr->pages[i],
- AGP_PAGE_DESTROY_FREE);
+ AGP_PAGE_DESTROY_FREE,
+ &curr->dma_addr[i]);
}
}
}
@@ -284,7 +286,8 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge,
}
for (i = 0; i < page_count; i++) {
- struct page *page = bridge->driver->agp_alloc_page(bridge);
+ struct page *page;
+ page = bridge->driver->agp_alloc_page(bridge, &new->dma_addr[i]);
if (page == NULL) {
agp_free_memory(new);
@@ -1257,11 +1260,11 @@ out:
}
EXPORT_SYMBOL(agp_generic_alloc_pages);
-struct page *agp_generic_alloc_page(struct agp_bridge_data *bridge)
+struct page *agp_generic_alloc_page(struct agp_bridge_data *bridge, dma_addr_t *dma_addr)
{
struct page * page;
- page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
+ page = _agp_alloc_page(agp_bridge, dma_addr);
if (page == NULL)
return NULL;
@@ -1299,7 +1302,7 @@ void agp_generic_destroy_pages(struct agp_memory *mem)
}
EXPORT_SYMBOL(agp_generic_destroy_pages);
-void agp_generic_destroy_page(struct page *page, int flags)
+void agp_generic_destroy_page(struct page *page, int flags, dma_addr_t *dma_addr)
{
if (page == NULL)
return;
@@ -1309,7 +1312,7 @@ void agp_generic_destroy_page(struct page *page, int flags)
if (flags & AGP_PAGE_DESTROY_FREE) {
put_page(page);
- __free_page(page);
+ _agp_free_page(agp_bridge, page, dma_addr);
atomic_dec(&agp_bridge->current_memory_agp);
}
}
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index 9272c38..dcd8894 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -364,9 +364,10 @@ static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type)
{
struct agp_memory *new;
struct page *page;
+ dma_addr_t dma_addr[4];
switch (pg_count) {
- case 1: page = agp_bridge->driver->agp_alloc_page(agp_bridge);
+ case 1: page = agp_bridge->driver->agp_alloc_page(agp_bridge, &dma_addr[0]);
break;
case 4:
/* kludge to get 4 physical pages for ARGB cursor */
@@ -428,9 +429,11 @@ static void intel_i810_free_by_type(struct agp_memory *curr)
i8xx_destroy_pages(curr->pages[0]);
else {
agp_bridge->driver->agp_destroy_page(curr->pages[0],
- AGP_PAGE_DESTROY_UNMAP);
+ AGP_PAGE_DESTROY_UNMAP,
+ &curr->dma_addr[0]);
agp_bridge->driver->agp_destroy_page(curr->pages[0],
- AGP_PAGE_DESTROY_FREE);
+ AGP_PAGE_DESTROY_FREE,
+ &curr->dma_addr[0]);
}
agp_free_page_array(curr);
}
diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c
index ffa888c..4436438 100644
--- a/drivers/char/agp/sgi-agp.c
+++ b/drivers/char/agp/sgi-agp.c
@@ -39,7 +39,8 @@ static struct aper_size_info_fixed sgi_tioca_sizes[] = {
{0, 0, 0},
};
-static struct page *sgi_tioca_alloc_page(struct agp_bridge_data *bridge)
+static struct page *sgi_tioca_alloc_page(struct agp_bridge_data *bridge,
+ dma_addr_t *dma_addr)
{
struct page *page;
int nid;
--
1.7.1
Set the dma_addr[i] to DMA_ERROR_CODE so it will be the
responsibility of the caller to set the dma_addr[i] to the right
value. Currently the callers that would utililize this are then ones
that set the type to AGP_USER_TYPES are the TTM and DRM APIs.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/generic.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index 534dccb..77ff3bd 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -1221,8 +1221,10 @@ struct agp_memory *agp_generic_alloc_user(size_t page_count, int type)
if (new == NULL)
return NULL;
- for (i = 0; i < page_count; i++)
+ for (i = 0; i < page_count; i++) {
new->pages[i] = NULL;
+ new->dma_addr[i] = DMA_ERROR_CODE;
+ }
new->page_count = 0;
new->type = type;
new->num_scratch_pages = pages;
--
1.7.1
From: Konrad Rzeszutek Wilk <[email protected]>
We turn the PCI API on for x86/x86_64 AGP drivers. This means that
any calls to:
agp_generic_insert_memory
agp_generic_create_gatt_table
agp_generic_free_gatt_table
agp_generic_alloc_pages
agp_generic_alloc_page
agp_generic_destroy_pages
agp_generic_destroy_page
Will have the 'struct agp_memory' variable dma_addr filled with the
DMA (bus) address of the page. For agp_generic_[alloc|destroy]_page
it requires that the DMA (bus) address be passed in as extra argument.
Note: This change goes only effect on X86/X86_64 platforms. All
other ones would still be using the old mechanism.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
arch/x86/include/asm/agp.h | 30 ++++++++++++++++++++----------
1 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/arch/x86/include/asm/agp.h b/arch/x86/include/asm/agp.h
index 72e7179..eb4e160 100644
--- a/arch/x86/include/asm/agp.h
+++ b/arch/x86/include/asm/agp.h
@@ -24,22 +24,32 @@
/* GATT allocation. Returns/accepts GATT kernel virtual address. */
#define alloc_gatt_pages(bridge, order) \
- ((char *)__get_free_pages(GFP_KERNEL, (order)))
+ ({ \
+ pci_alloc_consistent(bridge->dev, \
+ PAGE_SIZE * (1 << order), \
+ (dma_addr_t *)&bridge->gatt_bus_addr); \
+ })
#define free_gatt_pages(bridge, order) \
- free_pages((unsigned long)(bridge->gatt_table_real), (order))
+ pci_free_consistent(bridge->dev, bridge->gatt_table_size, \
+ bridge->gatt_table_real, \
+ bridge->gatt_bus_addr);
/* pages allocation. */
#define _agp_alloc_page(bridge, dma_addr) \
({ \
- struct page *_page = NULL; \
- _page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); \
- if (_page) \
- *dma_addr = page_to_phys(_page); \
+ void *_addr = NULL; \
+ struct page * _page = NULL;\
+ _addr = pci_alloc_consistent(bridge->dev, PAGE_SIZE, \
+ dma_addr); \
+ if (_addr) \
+ _page = virt_to_page(_addr); \
_page; \
})
#define _agp_free_page(bridge, page, dma_addr) \
- ({ \
- __free_page(page); \
- *dma_addr = DMA_ERROR_CODE; \
- })
+ ({ \
+ void *_addr = NULL; \
+ _addr = page_address(page); \
+ pci_free_consistent(bridge->dev, PAGE_SIZE, _addr, *dma_addr); \
+ *dma_addr = DMA_ERROR_CODE; \
+ })
#endif /* _ASM_X86_AGP_H */
--
1.7.1
The SGI Altix code sets its own GATT up, but rulies on
agp_generic_destroy_page[|s] functions, which use the PCI API.
Interestingly the sgi_tioca_alloc_page, which this patch removes,
had an optimization to allocate pages from a specific node. This optimization
also exists in the SGI sn/pci/pci_dma.c code when setting up a page
and since agp_generic_alloc_page uses that, we end up using it
in the generic allocation path.
The other change is that this patch is to utilize the dma_addr[]
field instead of doing page_to_phys(mem->pages[]) as that has
already been done in the PCI API.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
CC: Russ Anderson <[email protected]>
---
drivers/char/agp/sgi-agp.c | 22 ++--------------------
1 files changed, 2 insertions(+), 20 deletions(-)
diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c
index 4436438..fcaf16a 100644
--- a/drivers/char/agp/sgi-agp.c
+++ b/drivers/char/agp/sgi-agp.c
@@ -39,24 +39,6 @@ static struct aper_size_info_fixed sgi_tioca_sizes[] = {
{0, 0, 0},
};
-static struct page *sgi_tioca_alloc_page(struct agp_bridge_data *bridge,
- dma_addr_t *dma_addr)
-{
- struct page *page;
- int nid;
- struct tioca_kernel *info =
- (struct tioca_kernel *)bridge->dev_private_data;
-
- nid = info->ca_closest_node;
- page = alloc_pages_node(nid, GFP_KERNEL, 0);
- if (!page)
- return NULL;
-
- get_page(page);
- atomic_inc(&agp_bridge->current_memory_agp);
- return page;
-}
-
/*
* Flush GART tlb's. Cannot selectively flush based on memory so the mem
* arg is ignored.
@@ -192,7 +174,7 @@ static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start,
for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
table[j] =
bridge->driver->mask_memory(bridge,
- page_to_phys(mem->pages[i]),
+ mem->dma_addr[i],
mem->type);
}
@@ -264,7 +246,7 @@ const struct agp_bridge_driver sgi_tioca_driver = {
.remove_memory = sgi_tioca_remove_memory,
.alloc_by_type = agp_generic_alloc_by_type,
.free_by_type = agp_generic_free_by_type,
- .agp_alloc_page = sgi_tioca_alloc_page,
+ .agp_alloc_page = agp_generic_alloc_page,
.agp_destroy_page = agp_generic_destroy_page,
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
.cant_use_aperture = true,
--
1.7.1
We add to the macro an extra parameter: bridge. We will use
this new bridge parameter for the PCI API. Specifically
we will extract the 'struct pcidev' from it for the PCI API calls.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
arch/alpha/include/asm/agp.h | 6 +++---
arch/ia64/include/asm/agp.h | 6 +++---
arch/parisc/include/asm/agp.h | 6 +++---
arch/powerpc/include/asm/agp.h | 6 +++---
arch/sparc/include/asm/agp.h | 6 +++---
arch/x86/include/asm/agp.h | 6 +++---
drivers/char/agp/generic.c | 8 ++++----
7 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/arch/alpha/include/asm/agp.h b/arch/alpha/include/asm/agp.h
index a94d48b..0ee274c 100644
--- a/arch/alpha/include/asm/agp.h
+++ b/arch/alpha/include/asm/agp.h
@@ -10,9 +10,9 @@
#define flush_agp_cache() mb()
/* GATT allocation. Returns/accepts GATT kernel virtual address. */
-#define alloc_gatt_pages(order) \
+#define alloc_gatt_pages(bridge, order) \
((char *)__get_free_pages(GFP_KERNEL, (order)))
-#define free_gatt_pages(table, order) \
- free_pages((unsigned long)(table), (order))
+#define free_gatt_pages(bridge, order) \
+ free_pages((unsigned long)(bridge->gatt_table_real), (order))
#endif
diff --git a/arch/ia64/include/asm/agp.h b/arch/ia64/include/asm/agp.h
index 01d09c4..4e12b72 100644
--- a/arch/ia64/include/asm/agp.h
+++ b/arch/ia64/include/asm/agp.h
@@ -18,9 +18,9 @@
#define flush_agp_cache() mb()
/* GATT allocation. Returns/accepts GATT kernel virtual address. */
-#define alloc_gatt_pages(order) \
+#define alloc_gatt_pages(bridge, order) \
((char *)__get_free_pages(GFP_KERNEL, (order)))
-#define free_gatt_pages(table, order) \
- free_pages((unsigned long)(table), (order))
+#define free_gatt_pages(bridge, order) \
+ free_pages((unsigned long)(bridge->gatt_table_real), (order))
#endif /* _ASM_IA64_AGP_H */
diff --git a/arch/parisc/include/asm/agp.h b/arch/parisc/include/asm/agp.h
index d226ffa..6e8fd98 100644
--- a/arch/parisc/include/asm/agp.h
+++ b/arch/parisc/include/asm/agp.h
@@ -12,9 +12,9 @@
#define flush_agp_cache() mb()
/* GATT allocation. Returns/accepts GATT kernel virtual address. */
-#define alloc_gatt_pages(order) \
+#define alloc_gatt_pages(bridge, order) \
((char *)__get_free_pages(GFP_KERNEL, (order)))
-#define free_gatt_pages(table, order) \
- free_pages((unsigned long)(table), (order))
+#define free_gatt_pages(bridge, order) \
+ free_pages((unsigned long)(bridge->gatt_table_real), (order))
#endif /* _ASM_PARISC_AGP_H */
diff --git a/arch/powerpc/include/asm/agp.h b/arch/powerpc/include/asm/agp.h
index 416e12c..0996a43 100644
--- a/arch/powerpc/include/asm/agp.h
+++ b/arch/powerpc/include/asm/agp.h
@@ -9,10 +9,10 @@
#define flush_agp_cache() mb()
/* GATT allocation. Returns/accepts GATT kernel virtual address. */
-#define alloc_gatt_pages(order) \
+#define alloc_gatt_pages(bridge, order) \
((char *)__get_free_pages(GFP_KERNEL, (order)))
-#define free_gatt_pages(table, order) \
- free_pages((unsigned long)(table), (order))
+#define free_gatt_pages(bridge, order) \
+ free_pages((unsigned long)(bridge->gatt_table_real), (order))
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_AGP_H */
diff --git a/arch/sparc/include/asm/agp.h b/arch/sparc/include/asm/agp.h
index 70f52c1..2a83241 100644
--- a/arch/sparc/include/asm/agp.h
+++ b/arch/sparc/include/asm/agp.h
@@ -8,9 +8,9 @@
#define flush_agp_cache() mb()
/* GATT allocation. Returns/accepts GATT kernel virtual address. */
-#define alloc_gatt_pages(order) \
+#define alloc_gatt_pages(bridge, order) \
((char *)__get_free_pages(GFP_KERNEL, (order)))
-#define free_gatt_pages(table, order) \
- free_pages((unsigned long)(table), (order))
+#define free_gatt_pages(bridge, order) \
+ free_pages((unsigned long)(bridge->gatt_table_real), (order))
#endif
diff --git a/arch/x86/include/asm/agp.h b/arch/x86/include/asm/agp.h
index eec2a70..cbc996d 100644
--- a/arch/x86/include/asm/agp.h
+++ b/arch/x86/include/asm/agp.h
@@ -23,9 +23,9 @@
#define flush_agp_cache() wbinvd()
/* GATT allocation. Returns/accepts GATT kernel virtual address. */
-#define alloc_gatt_pages(order) \
+#define alloc_gatt_pages(bridge, order) \
((char *)__get_free_pages(GFP_KERNEL, (order)))
-#define free_gatt_pages(table, order) \
- free_pages((unsigned long)(table), (order))
+#define free_gatt_pages(bridge, order) \
+ free_pages((unsigned long)(bridge->gatt_table_real), (order))
#endif /* _ASM_X86_AGP_H */
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index 4956f1c..09332ba 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -931,7 +931,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
break;
}
- table = alloc_gatt_pages(page_order);
+ table = alloc_gatt_pages(bridge, page_order);
if (table == NULL) {
i++;
@@ -960,7 +960,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
size = ((struct aper_size_info_fixed *) temp)->size;
page_order = ((struct aper_size_info_fixed *) temp)->page_order;
num_entries = ((struct aper_size_info_fixed *) temp)->num_entries;
- table = alloc_gatt_pages(page_order);
+ table = alloc_gatt_pages(bridge, page_order);
}
if (table == NULL)
@@ -990,7 +990,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
ClearPageReserved(page);
- free_gatt_pages(table, page_order);
+ free_gatt_pages(bridge, page_order);
return -ENOMEM;
}
@@ -1052,7 +1052,7 @@ int agp_generic_free_gatt_table(struct agp_bridge_data *bridge)
for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
ClearPageReserved(page);
- free_gatt_pages(bridge->gatt_table_real, page_order);
+ free_gatt_pages(bridge, page_order);
agp_gatt_table = NULL;
bridge->gatt_table = NULL;
--
1.7.1
We turn the PCI API on for IA64 AGP drivers. This means that
any calls to:
agp_generic_insert_memory
agp_generic_create_gatt_table
agp_generic_free_gatt_table
agp_generic_alloc_pages
agp_generic_alloc_page
agp_generic_destroy_pages
agp_generic_destroy_page
Will have the 'struct agp_memory' variable dma_addr filled with the
DMA (bus) address of the page. For agp_generic_[alloc|destroy]_page
it requires that the DMA (bus) address be passed in as extra argument.
Note: This change goes only effects the IA64 platforms. All
other ones would still be using the old mechanism.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
arch/ia64/include/asm/agp.h | 31 ++++++++++++++++++++-----------
1 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/arch/ia64/include/asm/agp.h b/arch/ia64/include/asm/agp.h
index 9107e97..62abc81 100644
--- a/arch/ia64/include/asm/agp.h
+++ b/arch/ia64/include/asm/agp.h
@@ -19,23 +19,32 @@
/* GATT allocation. Returns/accepts GATT kernel virtual address. */
#define alloc_gatt_pages(bridge, order) \
- ((char *)__get_free_pages(GFP_KERNEL, (order)))
+ ({ \
+ pci_alloc_consistent(bridge->dev, \
+ PAGE_SIZE * (1 << order), \
+ (dma_addr_t *)&bridge->gatt_bus_addr); \
+ })
#define free_gatt_pages(bridge, order) \
- free_pages((unsigned long)(bridge->gatt_table_real), (order))
+ pci_free_consistent(bridge->dev, bridge->gatt_table_size, \
+ bridge->gatt_table_real, \
+ bridge->gatt_bus_addr);
/* pages allocation. */
#define _agp_alloc_page(bridge, dma_addr) \
({ \
- struct page *_page = NULL; \
- _page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); \
- if (_page) \
- *dma_addr = page_to_phys(_page); \
+ void *_addr = NULL; \
+ struct page * _page = NULL;\
+ _addr = pci_alloc_consistent(bridge->dev, PAGE_SIZE, \
+ dma_addr); \
+ if (_addr) \
+ _page = virt_to_page(_addr); \
_page; \
})
#define _agp_free_page(bridge, page, dma_addr) \
- ({ \
- __free_page(page); \
- *dma_addr = DMA_ERROR_CODE; \
- })
-
+ ({ \
+ void *_addr = NULL; \
+ _addr = page_address(page); \
+ pci_free_consistent(bridge->dev, PAGE_SIZE, _addr, *dma_addr); \
+ *dma_addr = DMA_ERROR_CODE; \
+ })
#endif /* _ASM_IA64_AGP_H */
--
1.7.1
The hp-agp.c code sets its own GATT up, but relies on
agp_generic_[alloc|destroy]_page[|s] functions, which use the PCI API.
Following the chain of functions that pci_alloc_consistent does
on a ZX1 w/ a SBA or w/ a SBA + SWIOTLB it ends up in either
sba_alloc_coherent or ia64_swiotlb_alloc_coherent. The SBA ends
up using alloc_pages_exact_node, while the other: __get_free_pages.
The dma_address they come up with is virt_to_phys(addr) or in case
of needing to use the SWIOTLB or SBA's pool, they give the DMA address
of the virtual pages in their pool.
Since the dma_addr[i] ends up having the value from virt_to_phys(addr)
or page_to_phys(addr), lets utilize the dma_addr[i] and stick
a WARN_ON just in case.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/hp-agp.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c
index 056b289..a19d599 100644
--- a/drivers/char/agp/hp-agp.c
+++ b/drivers/char/agp/hp-agp.c
@@ -362,7 +362,8 @@ hp_zx1_insert_memory (struct agp_memory *mem, off_t pg_start, int type)
for (i = 0, j = io_pg_start; i < mem->page_count; i++) {
unsigned long paddr;
- paddr = page_to_phys(mem->pages[i]);
+ paddr = (unsigned long)mem->dma_addr[i];
+ WARN_ON(page_to_phys(mem->pages[i]) != mem->dma_addr[i]);
for (k = 0;
k < hp->io_pages_per_kpage;
k++, j++, paddr += hp->io_page_size) {
--
1.7.1
From: Konrad Rzeszutek Wilk <[email protected]>
We need this extra value when we use the PCI API for
de-allocating the GART pages.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/agp.h | 1 +
drivers/char/agp/generic.c | 10 +++++-----
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h
index 6c60d68..6569d01 100644
--- a/drivers/char/agp/agp.h
+++ b/drivers/char/agp/agp.h
@@ -133,6 +133,7 @@ struct agp_bridge_data {
struct pci_dev *dev;
u32 __iomem *gatt_table;
u32 *gatt_table_real;
+ ssize_t gatt_table_size;
unsigned long scratch_page;
struct page *scratch_page_page;
dma_addr_t scratch_page_dma;
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index 4cf3f94..b21eebb 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -974,14 +974,14 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
if (table == NULL)
return -ENOMEM;
- table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
+ bridge->gatt_table_size = PAGE_SIZE * (1 << page_order);
+ table_end = table + bridge->gatt_table_size - 1;
for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
SetPageReserved(page);
bridge->gatt_table_real = (u32 *) table;
agp_gatt_table = (void *)table;
-
bridge->driver->cache_flush();
#ifdef CONFIG_X86
if (set_memory_uc((unsigned long)table, 1 << page_order))
@@ -990,7 +990,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
bridge->gatt_table = (void *)table;
#else
bridge->gatt_table = ioremap_nocache(virt_to_phys(table),
- (PAGE_SIZE * (1 << page_order)));
+ bridge->gatt_table_size);
bridge->driver->cache_flush();
#endif
@@ -1055,7 +1055,7 @@ int agp_generic_free_gatt_table(struct agp_bridge_data *bridge)
iounmap(bridge->gatt_table);
#endif
table = (char *) bridge->gatt_table_real;
- table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
+ table_end = table + bridge->gatt_table_size - 1;
for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
ClearPageReserved(page);
@@ -1066,7 +1066,7 @@ int agp_generic_free_gatt_table(struct agp_bridge_data *bridge)
bridge->gatt_table = NULL;
bridge->gatt_table_real = NULL;
bridge->gatt_bus_addr = 0;
-
+ bridge->gatt_table_size = 0;
return 0;
}
EXPORT_SYMBOL(agp_generic_free_gatt_table);
--
1.7.1
In case the platform does not use the PCI API, we want to
still use the old mechanism of saving the gatt_bus_addr.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/generic.c | 7 +++++--
1 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index b21eebb..534dccb 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -911,6 +911,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
temp = bridge->current_size;
size = page_order = num_entries = 0;
+ bridge->gatt_bus_addr = DMA_ERROR_CODE;
if (bridge->driver->size_type != FIXED_APER_SIZE) {
do {
switch (bridge->driver->size_type) {
@@ -999,10 +1000,12 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
ClearPageReserved(page);
free_gatt_pages(bridge, page_order);
-
+ bridge->gatt_bus_addr = DMA_ERROR_CODE;
return -ENOMEM;
}
- bridge->gatt_bus_addr = virt_to_phys(bridge->gatt_table_real);
+ /* If using PCI API, alloc_gatt_pages would fill in gatt_bus_addr. */
+ if (bridge->gatt_bus_addr == DMA_ERROR_CODE)
+ bridge->gatt_bus_addr = virt_to_phys(bridge->gatt_table_real);
/* AK: bogus, should encode addresses > 4GB */
for (i = 0; i < num_entries; i++) {
--
1.7.1
I enabled CONFIG_IA64_SGI_NV2 and got these compile errors:
/linux/arch/ia64/sn/pci/pcibr/pcibr_dma.c:237: error: conflicting types for 'sn_dma_flush'
/linux/arch/ia64/include/asm/sn/io.h:21: note: previous declaration of 'sn_dma_flush' was here
/linux/arch/ia64/sn/pci/pcibr/pcibr_dma.c:412: error: conflicting types for 'sn_dma_flush'
/linux/arch/ia64/include/asm/sn/io.h:21: note: previous declaration of 'sn_dma_flush' was here
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
CC: [email protected]
---
arch/ia64/include/asm/sn/io.h | 16 ++++++++--------
1 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/arch/ia64/include/asm/sn/io.h b/arch/ia64/include/asm/sn/io.h
index 41c73a7..238ee67 100644
--- a/arch/ia64/include/asm/sn/io.h
+++ b/arch/ia64/include/asm/sn/io.h
@@ -18,7 +18,7 @@ extern int num_cnodes;
#define __sn_mf_a() ia64_mfa()
-extern void sn_dma_flush(unsigned long);
+extern void sn_dma_flush(u64);
#define __sn_inb ___sn_inb
#define __sn_inw ___sn_inw
@@ -62,7 +62,7 @@ ___sn_inb (unsigned long port)
if ((addr = sn_io_addr(port))) {
ret = *addr;
__sn_mf_a();
- sn_dma_flush((unsigned long)addr);
+ sn_dma_flush((u64)addr);
}
return ret;
}
@@ -76,7 +76,7 @@ ___sn_inw (unsigned long port)
if ((addr = sn_io_addr(port))) {
ret = *addr;
__sn_mf_a();
- sn_dma_flush((unsigned long)addr);
+ sn_dma_flush((u64)addr);
}
return ret;
}
@@ -90,7 +90,7 @@ ___sn_inl (unsigned long port)
if ((addr = sn_io_addr(port))) {
ret = *addr;
__sn_mf_a();
- sn_dma_flush((unsigned long)addr);
+ sn_dma_flush((u64)addr);
}
return ret;
}
@@ -144,7 +144,7 @@ ___sn_readb (const volatile void __iomem *addr)
val = *(volatile unsigned char __force *)addr;
__sn_mf_a();
- sn_dma_flush((unsigned long)addr);
+ sn_dma_flush((u64)addr);
return val;
}
@@ -155,7 +155,7 @@ ___sn_readw (const volatile void __iomem *addr)
val = *(volatile unsigned short __force *)addr;
__sn_mf_a();
- sn_dma_flush((unsigned long)addr);
+ sn_dma_flush((u64)addr);
return val;
}
@@ -166,7 +166,7 @@ ___sn_readl (const volatile void __iomem *addr)
val = *(volatile unsigned int __force *)addr;
__sn_mf_a();
- sn_dma_flush((unsigned long)addr);
+ sn_dma_flush((u64)addr);
return val;
}
@@ -177,7 +177,7 @@ ___sn_readq (const volatile void __iomem *addr)
val = *(volatile unsigned long __force *)addr;
__sn_mf_a();
- sn_dma_flush((unsigned long)addr);
+ sn_dma_flush((u64)addr);
return val;
}
--
1.7.1
For platforms that are not using the PCI API the value
of dma_addr[i] is page_to_phys(mem->page[i]), which was
obtained by using the _agp_alloc_page macro.
This does mean that drivers that have their own .agp_create_page
functions and don't populate dma_addr[i] and use this
agp_generic_insert_memory need to have this right. Fortunatly,
all of the previous patches have done so. Just in case we have
a warning and end use the old mechanism to get the DMA address.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/generic.c | 7 ++++++-
1 files changed, 6 insertions(+), 1 deletions(-)
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index 65fd7c7..4cf3f94 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -1142,8 +1142,13 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
}
for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ dma_addr_t dma_addr = mem->dma_addr[i];
+ if (dma_addr == DMA_ERROR_CODE) {
+ WARN_ONCE(1, "Caller hasn't converted to DMA API!\n");
+ dma_addr = page_to_phys(mem->pages[i]);
+ }
writel(bridge->driver->mask_memory(bridge,
- page_to_phys(mem->pages[i]),
+ dma_addr,
mask_type),
bridge->gatt_table+j);
}
--
1.7.1
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/ati-agp.c | 18 ++++++++++++------
1 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c
index dc30e22..9c8d19d 100644
--- a/drivers/char/agp/ati-agp.c
+++ b/drivers/char/agp/ati-agp.c
@@ -44,19 +44,23 @@ static struct gatt_mask ati_generic_masks[] =
struct ati_page_map {
unsigned long *real;
unsigned long __iomem *remapped;
+ dma_addr_t dma_addr;
};
static struct _ati_generic_private {
volatile u8 __iomem *registers;
struct ati_page_map **gatt_pages;
int num_tables;
+ struct pci_dev *pcidev;
} ati_generic_private;
static int ati_create_page_map(struct ati_page_map *page_map)
{
int i, err = 0;
- page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL);
+ page_map->real = (unsigned long *) pci_alloc_consistent(ati_generic_private.pcidev,
+ PAGE_SIZE,
+ &page_map->dma_addr);
if (page_map->real == NULL)
return -ENOMEM;
@@ -77,7 +81,9 @@ static void ati_free_page_map(struct ati_page_map *page_map)
{
unmap_page_from_agp(virt_to_page(page_map->real));
set_memory_wb((unsigned long)page_map->real, 1);
- free_page((unsigned long) page_map->real);
+ pci_free_consistent(ati_generic_private.pcidev, PAGE_SIZE,
+ (void *)page_map->real, page_map->dma_addr);
+ page_map->dma_addr = DMA_ERROR_CODE;
}
@@ -302,7 +308,7 @@ static int ati_insert_memory(struct agp_memory * mem,
addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
cur_gatt = GET_GATT(addr);
writel(agp_bridge->driver->mask_memory(agp_bridge,
- page_to_phys(mem->pages[i]),
+ mem->dma_addr[i],
mem->type),
cur_gatt+GET_GATT_OFF(addr));
}
@@ -361,7 +367,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge)
agp_bridge->gatt_table_real = (u32 *)page_dir.real;
agp_bridge->gatt_table = (u32 __iomem *) page_dir.remapped;
- agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real);
+ agp_bridge->gatt_bus_addr = page_dir.dma_addr;
/* Write out the size register */
current_size = A_SIZE_LVL2(agp_bridge->current_size);
@@ -391,7 +397,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge)
/* Calculate the agp offset */
for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) {
- writel(virt_to_phys(ati_generic_private.gatt_pages[i]->real) | 1,
+ writel(ati_generic_private.gatt_pages[i]->dma_addr | 1,
page_dir.remapped+GET_PAGE_DIR_OFF(addr));
readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */
}
@@ -521,9 +527,9 @@ found:
bridge->capndx = cap_ptr;
bridge->driver = &ati_generic_bridge;
-
dev_info(&pdev->dev, "Ati %s chipset\n", devs[j].chipset_name);
+ ati_generic_private.pcidev = pdev;
/* Fill in the mode register */
pci_read_config_dword(pdev,
bridge->capndx+PCI_AGP_STATUS,
--
1.7.1
The i460-agp.c code sets its own GATT up, but relies on
agp_generic_[alloc|destroy]_page[|s] functions, which use the PCI API.
The code can support large-pages (more than 4KB) but the code is
disabled with "insane" comment so did not attempt to grok it.
Following the chain of functions that pci_alloc_consistent does
on a DIG platform it ends up using the SWIOTLB. The SWIOTLB calls
ia64_swiotlb_alloc_coherent which ends up calling alloc_page.
The dma_address they come up with is virt_to_phys(addr) or in case
of needing to use the SWIOTLB, it is the DMA address of the virtual
pages in SWIOTLB pool.
Since the dma_addr[i] ends up having the value from virt_to_phys(addr)
or page_to_phys(addr), lets utilize the dma_addr[i] and stick
a WARN_ON just in case.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/i460-agp.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c
index 75b763c..bfd9234 100644
--- a/drivers/char/agp/i460-agp.c
+++ b/drivers/char/agp/i460-agp.c
@@ -297,8 +297,8 @@ static int i460_insert_memory_small_io_page (struct agp_memory *mem,
int i, j, k, num_entries;
void *temp;
- pr_debug("i460_insert_memory_small_io_page(mem=%p, pg_start=%ld, type=%d, paddr0=0x%lx)\n",
- mem, pg_start, type, page_to_phys(mem->pages[0]));
+ pr_debug("i460_insert_memory_small_io_page(mem=%p, pg_start=%ld, type=%d, paddr0=0x%lx (0x%lx)\n",
+ mem, pg_start, type, page_to_phys(mem->pages[0]),(unsigned long)mem->dma_addr[0]);
if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES)
return -EINVAL;
@@ -325,7 +325,8 @@ static int i460_insert_memory_small_io_page (struct agp_memory *mem,
io_page_size = 1UL << I460_IO_PAGE_SHIFT;
for (i = 0, j = io_pg_start; i < mem->page_count; i++) {
- paddr = page_to_phys(mem->pages[i]);
+ paddr = mem->dma_addr[i];
+ WARN_ON(page_to_phys(mem->pages[i]) != paddr);
for (k = 0; k < I460_IOPAGES_PER_KPAGE; k++, j++, paddr += io_page_size)
WR_GATT(j, i460_mask_memory(agp_bridge, paddr, mem->type));
}
--
1.7.1
The dma_addr contains the same value as page_to_phys(page). That is
until the PCI API is enabled at which point the dma_addr will
contain the true DMA (bus) address - which on baremetal will in all
likehood be the same as page_to_phys(page). But under Xen, it might
be different.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/ali-agp.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c
index ede9d0a..92382d7 100644
--- a/drivers/char/agp/ali-agp.c
+++ b/drivers/char/agp/ali-agp.c
@@ -153,7 +153,7 @@ static struct page *m1541_alloc_page(struct agp_bridge_data *bridge,
pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp);
pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL,
(((temp & ALI_CACHE_FLUSH_ADDR_MASK) |
- page_to_phys(page)) | ALI_CACHE_FLUSH_EN ));
+ *dma_addr) | ALI_CACHE_FLUSH_EN ));
return page;
}
@@ -182,7 +182,7 @@ static void m1541_destroy_page(struct page *page, int flags,
pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp);
pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL,
(((temp & ALI_CACHE_FLUSH_ADDR_MASK) |
- page_to_phys(page)) | ALI_CACHE_FLUSH_EN));
+ *dma_addr) | ALI_CACHE_FLUSH_EN));
}
agp_generic_destroy_page(page, flags, dma_addr);
}
--
1.7.1
The dma_addr/gatt_bus_addr contains the same value as page_to_phys(page) and
virt_to_phys(agp_bridge->gatt_bus_addr) respectivly. That is
until the PCI API is enabled at which point the dma_addr will
contain the true DMA (bus) address - which on baremetal will in all
likehood be the same as page_to_phys(page)/virt_to_phys(..). However
under Xen it might be different.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/amd64-agp.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c
index 42396df..b28bd86 100644
--- a/drivers/char/agp/amd64-agp.c
+++ b/drivers/char/agp/amd64-agp.c
@@ -79,7 +79,7 @@ static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
tmp = agp_bridge->driver->mask_memory(agp_bridge,
- page_to_phys(mem->pages[i]),
+ mem->dma_addr[i],
mask_type);
BUG_ON(tmp & 0xffffff0000000ffcULL);
@@ -178,7 +178,7 @@ static const struct aper_size_info_32 amd_8151_sizes[7] =
static int amd_8151_configure(void)
{
- unsigned long gatt_bus = virt_to_phys(agp_bridge->gatt_table_real);
+ unsigned long gatt_bus = agp_bridge->gatt_bus_addr;
int i;
if (!k8_northbridges.gart_supported)
--
1.7.1
The AGP drivers for newer chipsets (ICH9 and higher) use
the PCI DMA and get the proper bus address.
For older "legacy" code (say ICH5), this has not been done.
To make those chipsets work properly, we need to program the
bus address of the pages in GATT with the correct address.
When running under Xen, the old trick of PFN << PAGE_SIZE == phys
does not work as the PFN is not neccessary equal to the "real" hardware
PFN (called 'MFN'). As such we need to use the PCI API.
Currently the code works alongside the newer code that
uses scatterlist. In the future we can squish those together.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/intel-gtt.c | 54 +++++++++++++++++++++++++++++++-----------
1 files changed, 40 insertions(+), 14 deletions(-)
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index dcd8894..e984559 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -248,17 +248,21 @@ static void intel_fake_agp_enable(struct agp_bridge_data *bridge, u32 mode)
}
/* Exists to support ARGB cursors */
-static struct page *i8xx_alloc_pages(void)
+static struct page *i8xx_alloc_pages(dma_addr_t *dma_addr)
{
struct page *page;
+ void *addr;
- page = alloc_pages(GFP_KERNEL | GFP_DMA32, 2);
- if (page == NULL)
+ addr = pci_alloc_consistent(intel_private.pcidev, 4 * PAGE_SIZE,
+ dma_addr);
+ if (addr == NULL)
return NULL;
-
+ page = virt_to_page(addr);
if (set_pages_uc(page, 4) < 0) {
set_pages_wb(page, 4);
- __free_pages(page, 2);
+ pci_free_consistent(intel_private.pcidev, 4 * PAGE_SIZE,
+ addr, *dma_addr);
+ *dma_addr = DMA_ERROR_CODE;
return NULL;
}
get_page(page);
@@ -266,14 +270,18 @@ static struct page *i8xx_alloc_pages(void)
return page;
}
-static void i8xx_destroy_pages(struct page *page)
+static void i8xx_destroy_pages(struct page *page, dma_addr_t *dma_addr)
{
+ void *addr;
if (page == NULL)
return;
set_pages_wb(page, 4);
put_page(page);
- __free_pages(page, 2);
+ addr = page_address(page);
+ pci_free_consistent(intel_private.pcidev, 4 * PAGE_SIZE,
+ addr, *dma_addr);
+ *dma_addr = DMA_ERROR_CODE;
atomic_dec(&agp_bridge->current_memory_agp);
}
@@ -322,8 +330,14 @@ static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start,
if (!mem->is_flushed)
global_cache_flush();
for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ dma_addr_t addr = mem->dma_addr[i];
+ if (addr == DMA_ERROR_CODE) {
+ WARN_ONCE(1, "Caller hasn't converted to DMA" \
+ "API!\n");
+ addr = page_to_phys(mem->pages[i]);
+ }
writel(agp_bridge->driver->mask_memory(agp_bridge,
- page_to_phys(mem->pages[i]), mask_type),
+ addr, mask_type),
intel_private.registers+I810_PTE_BASE+(j*4));
}
readl(intel_private.registers+I810_PTE_BASE+((j-1)*4));
@@ -371,7 +385,7 @@ static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type)
break;
case 4:
/* kludge to get 4 physical pages for ARGB cursor */
- page = i8xx_alloc_pages();
+ page = i8xx_alloc_pages(&dma_addr[0]);
break;
default:
return NULL;
@@ -385,11 +399,15 @@ static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type)
return NULL;
new->pages[0] = page;
+ new->dma_addr[0] = dma_addr[0];
if (pg_count == 4) {
/* kludge to get 4 physical pages for ARGB cursor */
new->pages[1] = new->pages[0] + 1;
new->pages[2] = new->pages[1] + 1;
new->pages[3] = new->pages[2] + 1;
+ new->dma_addr[1] = dma_addr[0] + PAGE_SIZE;
+ new->dma_addr[2] = dma_addr[1] + PAGE_SIZE;
+ new->dma_addr[3] = dma_addr[2] + PAGE_SIZE;
}
new->page_count = pg_count;
new->num_scratch_pages = pg_count;
@@ -426,7 +444,7 @@ static void intel_i810_free_by_type(struct agp_memory *curr)
agp_free_key(curr->key);
if (curr->type == AGP_PHYS_MEMORY) {
if (curr->page_count == 4)
- i8xx_destroy_pages(curr->pages[0]);
+ i8xx_destroy_pages(curr->pages[0], &curr->dma_addr[0]);
else {
agp_bridge->driver->agp_destroy_page(curr->pages[0],
AGP_PAGE_DESTROY_UNMAP,
@@ -451,10 +469,13 @@ static int intel_gtt_setup_scratch_page(void)
{
struct page *page;
dma_addr_t dma_addr;
+ void *addr;
- page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
- if (page == NULL)
+ addr = pci_alloc_consistent(intel_private.pcidev, PAGE_SIZE, &dma_addr);
+ if (addr == NULL)
return -ENOMEM;
+
+ page = virt_to_page(addr);
get_page(page);
set_pages_uc(page, 1);
@@ -466,7 +487,7 @@ static int intel_gtt_setup_scratch_page(void)
intel_private.scratch_page_dma = dma_addr;
} else
- intel_private.scratch_page_dma = page_to_phys(page);
+ intel_private.scratch_page_dma = dma_addr;
intel_private.scratch_page = page;
@@ -1031,7 +1052,12 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
pg_start, type);
} else {
for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
- dma_addr_t addr = page_to_phys(mem->pages[i]);
+ dma_addr_t addr = mem->dma_addr[i];
+ if (addr == DMA_ERROR_CODE) {
+ WARN_ONCE(1, "Caller hasn't converted to DMA "\
+ "API!\n");
+ addr = page_to_phys(mem->pages[i]);
+ }
intel_private.driver->write_entry(addr,
j, type);
}
--
1.7.1
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/sworks-agp.c | 18 ++++++++++++------
1 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c
index 13acaaf..494cc65 100644
--- a/drivers/char/agp/sworks-agp.c
+++ b/drivers/char/agp/sworks-agp.c
@@ -31,6 +31,7 @@
struct serverworks_page_map {
unsigned long *real;
unsigned long __iomem *remapped;
+ dma_addr_t dma_addr;
};
static struct _serverworks_private {
@@ -48,7 +49,10 @@ static int serverworks_create_page_map(struct serverworks_page_map *page_map)
{
int i;
- page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL);
+ page_map->real = (unsigned long *)pci_alloc_consistent(serverworks_private.svrwrks_dev,
+ PAGE_SIZE,
+ &page_map->dma_addr);
+
if (page_map->real == NULL) {
return -ENOMEM;
}
@@ -66,7 +70,9 @@ static int serverworks_create_page_map(struct serverworks_page_map *page_map)
static void serverworks_free_page_map(struct serverworks_page_map *page_map)
{
set_memory_wb((unsigned long)page_map->real, 1);
- free_page((unsigned long) page_map->real);
+ pci_free_consistent(serverworks_private.svrwrks_dev, PAGE_SIZE,
+ (void *) page_map->real, page_map->dma_addr);
+ page_map->dma_addr = DMA_ERROR_CODE;
}
static void serverworks_free_gatt_pages(void)
@@ -155,7 +161,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge)
/* Create a fake scratch directory */
for (i = 0; i < 1024; i++) {
writel(agp_bridge->scratch_page, serverworks_private.scratch_dir.remapped+i);
- writel(virt_to_phys(serverworks_private.scratch_dir.real) | 1, page_dir.remapped+i);
+ writel(serverworks_private.scratch_dir.dma_addr | 1, page_dir.remapped+i);
}
retval = serverworks_create_gatt_pages(value->num_entries / 1024);
@@ -167,7 +173,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge)
agp_bridge->gatt_table_real = (u32 *)page_dir.real;
agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped;
- agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real);
+ agp_bridge->gatt_bus_addr = page_dir.dma_addr;
/* Get the address for the gart region.
* This is a bus address even on the alpha, b/c its
@@ -179,7 +185,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge)
/* Calculate the agp offset */
for (i = 0; i < value->num_entries / 1024; i++)
- writel(virt_to_phys(serverworks_private.gatt_pages[i]->real)|1, page_dir.remapped+i);
+ writel(serverworks_private.gatt_pages[i]->dma_addr|1, page_dir.remapped+i);
return 0;
}
@@ -350,7 +356,7 @@ static int serverworks_insert_memory(struct agp_memory *mem,
addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
cur_gatt = SVRWRKS_GET_GATT(addr);
writel(agp_bridge->driver->mask_memory(agp_bridge,
- page_to_phys(mem->pages[i]), mem->type),
+ mem->dma_addr[i], mem->type),
cur_gatt+GET_GATT_OFF(addr));
}
serverworks_tlbflush(mem);
--
1.7.1
This piggybacks on git commit 8fd524b355daef0945692227e726fb444cebcd4f
("x86: Kill bad_dma_address variable") wherein we use now the
dma_map_ops->mapping_error to check for errors and the standard
check is against DMA_ERROR_CODE. Introduce it to the IA64 world.
CC: FUJITA Tomonori <[email protected]>
CC: Jesse Barnes <[email protected]>
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
arch/ia64/include/asm/dma-mapping.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/arch/ia64/include/asm/dma-mapping.h b/arch/ia64/include/asm/dma-mapping.h
index a2e7368..4336d08 100644
--- a/arch/ia64/include/asm/dma-mapping.h
+++ b/arch/ia64/include/asm/dma-mapping.h
@@ -12,6 +12,8 @@
#define ARCH_HAS_DMA_GET_REQUIRED_MASK
+#define DMA_ERROR_CODE 0
+
extern struct dma_map_ops *dma_ops;
extern struct ia64_machine_vector ia64_mv;
extern void set_iommu_machvec(void);
--
1.7.1
We are expanding the 'struct agp_memory' with an extra array
of dma_addr_t values. They correspond 1-to-1 to the page* array.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/generic.c | 9 +++++++--
include/linux/agp_backend.h | 4 +++-
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index 09332ba..bcefc66 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -97,11 +97,14 @@ EXPORT_SYMBOL(agp_flush_chipset);
void agp_alloc_page_array(size_t size, struct agp_memory *mem)
{
mem->pages = NULL;
-
- if (size <= 2*PAGE_SIZE)
+ mem->dma_addr = NULL;
+ if (size <= 2*PAGE_SIZE) {
mem->pages = kmalloc(size, GFP_KERNEL | __GFP_NOWARN);
+ mem->dma_addr = kmalloc(size, GFP_KERNEL | __GFP_NOWARN);
+ }
if (mem->pages == NULL) {
mem->pages = vmalloc(size);
+ mem->dma_addr = vmalloc(size);
}
}
EXPORT_SYMBOL(agp_alloc_page_array);
@@ -110,8 +113,10 @@ void agp_free_page_array(struct agp_memory *mem)
{
if (is_vmalloc_addr(mem->pages)) {
vfree(mem->pages);
+ vfree(mem->dma_addr);
} else {
kfree(mem->pages);
+ kfree(mem->dma_addr);
}
}
EXPORT_SYMBOL(agp_free_page_array);
diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h
index 09ea4a1..1d0d73f 100644
--- a/include/linux/agp_backend.h
+++ b/include/linux/agp_backend.h
@@ -81,9 +81,11 @@ struct agp_memory {
bool is_flushed;
/* list of agp_memory mapped to the aperture */
struct list_head mapped_list;
- /* DMA-mapped addresses */
+ /* DMA-mapped addresses (exclusivly used by intel-gtt) */
struct scatterlist *sg_list;
int num_sg;
+ /* DMA address for pages. */
+ dma_addr_t *dma_addr;
};
#define AGP_NORMAL_MEMORY 0
--
1.7.1
This macro will be used in the agp_generic_[alloc|destroy]_page[s|]
functions. Currently it does exactly what it replaces and also
saves the DMA address in dma_addr[] using the page_to_phys() macro.
These two macros can be at any time replaced with an implementation that
can utilize the PCI API.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
arch/alpha/include/asm/agp.h | 14 ++++++++++++++
arch/ia64/include/asm/agp.h | 15 +++++++++++++++
arch/parisc/include/asm/agp.h | 15 +++++++++++++++
arch/powerpc/include/asm/agp.h | 15 +++++++++++++++
arch/sparc/include/asm/agp.h | 14 ++++++++++++++
arch/x86/include/asm/agp.h | 14 ++++++++++++++
6 files changed, 87 insertions(+), 0 deletions(-)
diff --git a/arch/alpha/include/asm/agp.h b/arch/alpha/include/asm/agp.h
index 0ee274c..41547e3 100644
--- a/arch/alpha/include/asm/agp.h
+++ b/arch/alpha/include/asm/agp.h
@@ -15,4 +15,18 @@
#define free_gatt_pages(bridge, order) \
free_pages((unsigned long)(bridge->gatt_table_real), (order))
+/* pages allocation. */
+#define _agp_alloc_page(bridge, dma_addr) \
+ ({ \
+ struct page *_page = NULL; \
+ _page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); \
+ if (_page) \
+ *dma_addr = page_to_phys(_page); \
+ _page; \
+ })
+#define _agp_free_page(bridge, page, dma_addr) \
+ ({ \
+ __free_page(page); \
+ *dma_addr = DMA_ERROR_CODE; \
+ })
#endif
diff --git a/arch/ia64/include/asm/agp.h b/arch/ia64/include/asm/agp.h
index 4e12b72..9107e97 100644
--- a/arch/ia64/include/asm/agp.h
+++ b/arch/ia64/include/asm/agp.h
@@ -23,4 +23,19 @@
#define free_gatt_pages(bridge, order) \
free_pages((unsigned long)(bridge->gatt_table_real), (order))
+/* pages allocation. */
+#define _agp_alloc_page(bridge, dma_addr) \
+ ({ \
+ struct page *_page = NULL; \
+ _page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); \
+ if (_page) \
+ *dma_addr = page_to_phys(_page); \
+ _page; \
+ })
+#define _agp_free_page(bridge, page, dma_addr) \
+ ({ \
+ __free_page(page); \
+ *dma_addr = DMA_ERROR_CODE; \
+ })
+
#endif /* _ASM_IA64_AGP_H */
diff --git a/arch/parisc/include/asm/agp.h b/arch/parisc/include/asm/agp.h
index 6e8fd98..1de4174 100644
--- a/arch/parisc/include/asm/agp.h
+++ b/arch/parisc/include/asm/agp.h
@@ -17,4 +17,19 @@
#define free_gatt_pages(bridge, order) \
free_pages((unsigned long)(bridge->gatt_table_real), (order))
+/* pages allocation. */
+#define _agp_alloc_page(bridge, dma_addr) \
+ ({ \
+ struct page *_page = NULL; \
+ _page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); \
+ if (_page) \
+ *dma_addr = page_to_phys(_page); \
+ _page; \
+ })
+#define _agp_free_page(bridge, page, dma_addr) \
+ ({ \
+ __free_page(page); \
+ *dma_addr = DMA_ERROR_CODE; \
+ })
+
#endif /* _ASM_PARISC_AGP_H */
diff --git a/arch/powerpc/include/asm/agp.h b/arch/powerpc/include/asm/agp.h
index 0996a43..cdfa8e7 100644
--- a/arch/powerpc/include/asm/agp.h
+++ b/arch/powerpc/include/asm/agp.h
@@ -14,5 +14,20 @@
#define free_gatt_pages(bridge, order) \
free_pages((unsigned long)(bridge->gatt_table_real), (order))
+/* pages allocation. */
+#define _agp_alloc_page(bridge, dma_addr) \
+ ({ \
+ struct page *_page = NULL; \
+ _page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); \
+ if (_page) \
+ *dma_addr = page_to_phys(_page); \
+ _page; \
+ })
+#define _agp_free_page(bridge, page, dma_addr) \
+ ({ \
+ __free_page(page); \
+ *dma_addr = DMA_ERROR_CODE; \
+ })
+
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_AGP_H */
diff --git a/arch/sparc/include/asm/agp.h b/arch/sparc/include/asm/agp.h
index 2a83241..b0cd187 100644
--- a/arch/sparc/include/asm/agp.h
+++ b/arch/sparc/include/asm/agp.h
@@ -13,4 +13,18 @@
#define free_gatt_pages(bridge, order) \
free_pages((unsigned long)(bridge->gatt_table_real), (order))
+/* pages allocation. */
+#define _agp_alloc_page(bridge, dma_addr) \
+ ({ \
+ struct page *_page = NULL; \
+ _page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); \
+ if (_page) \
+ *dma_addr = page_to_phys(_page); \
+ _page; \
+ })
+#define _agp_free_page(bridge, page, dma_addr) \
+ ({ \
+ __free_page(page); \
+ *dma_addr = DMA_ERROR_CODE; \
+ })
#endif
diff --git a/arch/x86/include/asm/agp.h b/arch/x86/include/asm/agp.h
index cbc996d..72e7179 100644
--- a/arch/x86/include/asm/agp.h
+++ b/arch/x86/include/asm/agp.h
@@ -28,4 +28,18 @@
#define free_gatt_pages(bridge, order) \
free_pages((unsigned long)(bridge->gatt_table_real), (order))
+/* pages allocation. */
+#define _agp_alloc_page(bridge, dma_addr) \
+ ({ \
+ struct page *_page = NULL; \
+ _page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); \
+ if (_page) \
+ *dma_addr = page_to_phys(_page); \
+ _page; \
+ })
+#define _agp_free_page(bridge, page, dma_addr) \
+ ({ \
+ __free_page(page); \
+ *dma_addr = DMA_ERROR_CODE; \
+ })
#endif /* _ASM_X86_AGP_H */
--
1.7.1
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/amd-k7-agp.c | 20 ++++++++++++++------
1 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c
index b1b4362..4be8f65 100644
--- a/drivers/char/agp/amd-k7-agp.c
+++ b/drivers/char/agp/amd-k7-agp.c
@@ -25,19 +25,23 @@ static struct pci_device_id agp_amdk7_pci_table[];
struct amd_page_map {
unsigned long *real;
unsigned long __iomem *remapped;
+ dma_addr_t dma_addr;
};
static struct _amd_irongate_private {
volatile u8 __iomem *registers;
struct amd_page_map **gatt_pages;
int num_tables;
+ struct pci_dev *pcidev;
} amd_irongate_private;
static int amd_create_page_map(struct amd_page_map *page_map)
{
int i;
- page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL);
+ page_map->real = (unsigned long *)pci_alloc_consistent(amd_irongate_private.pcidev,
+ PAGE_SIZE,
+ &page_map->dma_addr);
if (page_map->real == NULL)
return -ENOMEM;
@@ -48,7 +52,8 @@ static int amd_create_page_map(struct amd_page_map *page_map)
PAGE_SIZE);
if (page_map->remapped == NULL) {
ClearPageReserved(virt_to_page(page_map->real));
- free_page((unsigned long) page_map->real);
+ pci_free_consistent(amd_irongate_private.pcidev, PAGE_SIZE,
+ (void *) page_map->real, page_map->dma_addr);
page_map->real = NULL;
return -ENOMEM;
}
@@ -74,7 +79,9 @@ static void amd_free_page_map(struct amd_page_map *page_map)
#else
set_memory_wb((unsigned long)page_map->real, 1);
#endif
- free_page((unsigned long) page_map->real);
+ pci_free_consistent(amd_irongate_private.pcidev, PAGE_SIZE,
+ (void *) page_map->real, page_map->dma_addr);
+ page_map->dma_addr = DMA_ERROR_CODE;
}
static void amd_free_gatt_pages(void)
@@ -161,7 +168,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge)
agp_bridge->gatt_table_real = (u32 *)page_dir.real;
agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped;
- agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real);
+ agp_bridge->gatt_bus_addr = page_dir.dma_addr;
/* Get the address for the gart region.
* This is a bus address even on the alpha, b/c its
@@ -174,7 +181,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge)
/* Calculate the agp offset */
for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) {
- writel(virt_to_phys(amd_irongate_private.gatt_pages[i]->real) | 1,
+ writel(amd_irongate_private.gatt_pages[i]->dma_addr | 1,
page_dir.remapped+GET_PAGE_DIR_OFF(addr));
readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */
}
@@ -334,7 +341,7 @@ static int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
cur_gatt = GET_GATT(addr);
writel(agp_generic_mask_memory(agp_bridge,
- page_to_phys(mem->pages[i]),
+ mem->dma_addr[i],
mem->type),
cur_gatt+GET_GATT_OFF(addr));
readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */
@@ -447,6 +454,7 @@ static int __devinit agp_amdk7_probe(struct pci_dev *pdev,
bridge->dev_private_data = &amd_irongate_private,
bridge->dev = pdev;
bridge->capndx = cap_ptr;
+ amd_irongate_private.pcidev = bridge->dev;
/* 751 Errata (22564_B-1.PDF)
erratum 20: strobe glitch with Nvidia NV10 GeForce cards.
--
1.7.1
The dma_addr contains the same value as page_to_phys(page). That is
until the PCI API is enabled at which point the dma_addr will
contain the true DMA (bus) address - which on baremetal will in all
likehood be the same as page_to_phys(page). But under Xen it might
be different.
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/nvidia-agp.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c
index b9734a9..4b4993f 100644
--- a/drivers/char/agp/nvidia-agp.c
+++ b/drivers/char/agp/nvidia-agp.c
@@ -224,7 +224,7 @@ static int nvidia_insert_memory(struct agp_memory *mem, off_t pg_start, int type
}
for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
writel(agp_bridge->driver->mask_memory(agp_bridge,
- page_to_phys(mem->pages[i]), mask_type),
+ mem->dma_addr[i], mask_type),
agp_bridge->gatt_table+nvidia_private.pg_offset+j);
}
--
1.7.1
This way when we want to opt in a platform to use the PCI DMA it
is just the matter of changing the implementation in
arch/<platform>/include/asm/agp.h
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
---
drivers/char/agp/generic.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index bcefc66..cf465b5 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -1233,7 +1233,7 @@ int agp_generic_alloc_pages(struct agp_bridge_data *bridge, struct agp_memory *m
int i, ret = -ENOMEM;
for (i = 0; i < num_pages; i++) {
- page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
+ page = _agp_alloc_page(agp_bridge, &mem->dma_addr[i]);
/* agp_free_memory() needs gart address */
if (page == NULL)
goto out;
@@ -1292,7 +1292,7 @@ void agp_generic_destroy_pages(struct agp_memory *mem)
unmap_page_from_agp(page);
#endif
put_page(page);
- __free_page(page);
+ _agp_free_page(agp_bridge, page, &mem->dma_addr[i]);
atomic_dec(&agp_bridge->current_memory_agp);
mem->pages[i] = NULL;
}
--
1.7.1
On Mon, 2010-12-06 at 18:24 -0500, Konrad Rzeszutek Wilk wrote:
> Attached is a set of RFC patches that make it possible for AGP graphic drivers to
> work under Xen. The major problem that Linux kernel has when running under Xen
> is that the usage of "virt_to_phys(x) >> PAGE_SIZE" to get the DMA address is not
> applicable. That is due to the fact that the PFN value is not the real Machine
> Frame Number (MFN), hence virt_to_phys(x) >> PAGE_SIZE ends up pointing to a
> random physical address. But if you use the PCI API, then the DMA (bus) address
> returned is a valid MFN.
Can I ask you to go back a step and address what the use case for all of
this is, the patch description doesn't address why anyone cares about
AGP in 2010, esp with Xen. Virtualising hw drivers for the sake of it
is all well and good, but since most of these drivers are for really
legacy systems I can't imagine we are going to see a lot of regression
testing before they hit distros like Debian two years from now, though
maybe Gentoo might pick up some, (is anyone even running IA64?).
I can maybe imagine the Intel GTT being cared about but we've already
addressed the issues in it from what I can see.
Also the X server use case is still possibly valid for a lot of the
systems here, its userspace ABI so it can't just end up broken. The move
to TTM/DRM being the main user didn't suddenly remove the use of the X
server case on older systems which don't have a TTM/DRM layer.
Other than that the idea seems sane, I just hate having to upgrade large
parts of the subsystem without some reasonable justification that
someone out there is going to use it. If it allows some major cleanup
else where that would also be a possibly good justification.
Dave.
On Tue, Dec 07, 2010 at 10:02:06AM +1000, Dave Airlie wrote:
> On Mon, 2010-12-06 at 18:24 -0500, Konrad Rzeszutek Wilk wrote:
> > Attached is a set of RFC patches that make it possible for AGP graphic drivers to
> > work under Xen. The major problem that Linux kernel has when running under Xen
> > is that the usage of "virt_to_phys(x) >> PAGE_SIZE" to get the DMA address is not
> > applicable. That is due to the fact that the PFN value is not the real Machine
> > Frame Number (MFN), hence virt_to_phys(x) >> PAGE_SIZE ends up pointing to a
> > random physical address. But if you use the PCI API, then the DMA (bus) address
> > returned is a valid MFN.
>
> Can I ask you to go back a step and address what the use case for all of
> this is, the patch description doesn't address why anyone cares about
> AGP in 2010, esp with Xen. Virtualising hw drivers for the sake of it
> is all well and good, but since most of these drivers are for really
> legacy systems I can't imagine we are going to see a lot of regression
> testing before they hit distros like Debian two years from now, though
> maybe Gentoo might pick up some, (is anyone even running IA64?).
Hey Dave,
Thank you for you quick reply and brief look at the code. When I wrote
this code a similar thought went through my head, and around that
time three bugs from Debian (#601341, #602418 and #604096) asked what
is the the status of making AGP work with Xen. I've asked the folks
on the bugs to provide input on your question. In regards to regressions
I've a lot of these machines so I can definitly test them for
regressions (my thought was to run the OpenArena, TuxRace, and watch
some flash - is that a good enough test or should I expand it?)
But obviously I don't have all of them so yeah something might break.
>
> I can maybe imagine the Intel GTT being cared about but we've already
> addressed the issues in it from what I can see.
Definitly for the new hardware.
>
> Also the X server use case is still possibly valid for a lot of the
> systems here, its userspace ABI so it can't just end up broken. The move
Right. That would mean testing the MGA, SiS, VIA, and whatever else in the
drivers/gpu/drm/ directory with Xserver. Would the latest Xorg (1.8) do?
> to TTM/DRM being the main user didn't suddenly remove the use of the X
> server case on older systems which don't have a TTM/DRM layer.
Yup. I've some patches for that too, but figured it would make sense
to get input first on this set. Let me post them shortly as they can
be used independently of this patchset.
>
> Other than that the idea seems sane, I just hate having to upgrade large
> parts of the subsystem without some reasonable justification that
Naturally.
> someone out there is going to use it. If it allows some major cleanup
> else where that would also be a possibly good justification.
>
> Dave.
>
>
On Mon, 2010-12-06 at 19:48 -0500, Konrad Rzeszutek Wilk wrote:
> On Tue, Dec 07, 2010 at 10:02:06AM +1000, Dave Airlie wrote:
> > On Mon, 2010-12-06 at 18:24 -0500, Konrad Rzeszutek Wilk wrote:
> > > Attached is a set of RFC patches that make it possible for AGP graphic drivers to
> > > work under Xen. The major problem that Linux kernel has when running under Xen
> > > is that the usage of "virt_to_phys(x) >> PAGE_SIZE" to get the DMA address is not
> > > applicable. That is due to the fact that the PFN value is not the real Machine
> > > Frame Number (MFN), hence virt_to_phys(x) >> PAGE_SIZE ends up pointing to a
> > > random physical address. But if you use the PCI API, then the DMA (bus) address
> > > returned is a valid MFN.
> >
> > Can I ask you to go back a step and address what the use case for all of
> > this is, the patch description doesn't address why anyone cares about
> > AGP in 2010, esp with Xen. Virtualising hw drivers for the sake of it
> > is all well and good, but since most of these drivers are for really
> > legacy systems I can't imagine we are going to see a lot of regression
> > testing before they hit distros like Debian two years from now, though
> > maybe Gentoo might pick up some, (is anyone even running IA64?).
>
> Hey Dave,
> Thank you for you quick reply and brief look at the code. When I wrote
> this code a similar thought went through my head, and around that
> time three bugs from Debian (#601341, #602418 and #604096) asked what
> is the the status of making AGP work with Xen. I've asked the folks
> on the bugs to provide input on your question. In regards to regressions
> I've a lot of these machines so I can definitly test them for
> regressions (my thought was to run the OpenArena, TuxRace, and watch
> some flash - is that a good enough test or should I expand it?)
> But obviously I don't have all of them so yeah something might break.
Okay reading that I'm not sure you've started at the right end.
I just read 601341, and it concerns nouveau, which shouldn't on the hw
described be touching AGP at all. Its a PCIE machine, so the DRM/TTM
code won't use any of the AGP paths.
Now if we have fixes for TTM/nouveau/radeon that make it work under Xen
then that is a whole different story, however these patches are not
those.
I'd suggest you reverse the priority, of the comment
a) Fix the GART/AGP backend (so drivers/char/agp/*.c) so they use the
PCI API.
Only the i915 and higher are using the PCI API and I've some of the older
boxes with i860 so can actually test it.
b) Fix the TTM to use the DMA API.
c) Lastly, get rid of _PAGE_IOMAP so we don't have to depend on radeon/nouveau/etc
to set the proper _PAGE_IOMAP on the PFNs/BARs..
to do c, then b, and finally figure out if anyone cares about a, unless there is a major dependency in the code that requires this ordering.
Dave.
> Okay reading that I'm not sure you've started at the right end.
I actually have all of those patches ready but figured I would start
from the AGP side..
>
> I just read 601341, and it concerns nouveau, which shouldn't on the hw
> described be touching AGP at all. Its a PCIE machine, so the DRM/TTM
> code won't use any of the AGP paths.
I think most of them do touch PCIe cards. I swore that there were
some AGP users out there, maybe they aren't on those bugs - let me search
my mail archive. Maybe it just me who still uses AGP...
>
> Now if we have fixes for TTM/nouveau/radeon that make it work under Xen
> then that is a whole different story, however these patches are not
> those.
Nope. I just posted them: https://lkml.org/lkml/2010/12/6/516
>
> I'd suggest you reverse the priority, of the comment
>
> a) Fix the GART/AGP backend (so drivers/char/agp/*.c) so they use the
> PCI API.
> Only the i915 and higher are using the PCI API and I've some of the older
> boxes with i860 so can actually test it.
> b) Fix the TTM to use the DMA API.
> c) Lastly, get rid of _PAGE_IOMAP so we don't have to depend on radeon/nouveau/etc
> to set the proper _PAGE_IOMAP on the PFNs/BARs..
>
> to do c, then b, and finally figure out if anyone cares about a, unless there is a major dependency in the code that requires this ordering.
There is a patch for c) that is nice and simple:
http://git.kernel.org/?p=linux/kernel/git/konrad/xen.git;a=commit;h=f5d630621f6616a8a14438c047332742e77c0739
but we are working on making that obsolete by .. well I can go in details
but it still is just on pieces of paper.
>
> Dave.
> I think most of them do touch PCIe cards. I swore that there were
> some AGP users out there, maybe they aren't on those bugs - let me search
> my mail archive. Maybe it just me who still uses AGP...
Ah, found three individuals :-) If none of them strongly object I am going
to freeze the AGP patchset and not bother with it for upstreaming.
> >
> > Now if we have fixes for TTM/nouveau/radeon that make it work under Xen
> > then that is a whole different story, however these patches are not
> > those.
>
> Nope. I just posted them: https://lkml.org/lkml/2010/12/6/516
Dave,
If you have some time, I would very much appreciate you looking over those
TTM patches. I hope they are sane enough, and any recommendation on how to
make them even better would be quite appreciated.