Commit a030ce44 authored by Thomas Hellstrom's avatar Thomas Hellstrom Committed by Dave Jones

[AGPGART] Allow drm-populated agp memory types

This patch allows drm to populate an agpgart structure with pages of its own.
It's needed for the new drm memory manager which dynamically flips pages in and out of AGP.

The patch modifies the generic functions as well as the intel agp driver. The intel drm driver is
currently the only one supporting the new memory manager.

Other agp drivers may need some minor fixing up once they have a corresponding memory manager enabled drm driver.

AGP memory types >= AGP_USER_TYPES are not populated by the agpgart driver, but the drm is expected
to do that, as well as taking care of cache- and tlb flushing when needed.

It's not possible to request these types from user space using agpgart ioctls.

The Intel driver also gets a new memory type for pages that can be bound cached to the intel GTT.
Signed-off-by: default avatarThomas Hellstrom <thomas@tungstengraphics.com>
Signed-off-by: default avatarDave Jones <davej@redhat.com>
parent 0316fe83
......@@ -114,6 +114,7 @@ struct agp_bridge_driver {
void (*free_by_type)(struct agp_memory *);
void *(*agp_alloc_page)(struct agp_bridge_data *);
void (*agp_destroy_page)(void *);
int (*agp_type_to_mask_type) (struct agp_bridge_data *, int);
};
struct agp_bridge_data {
......@@ -218,6 +219,7 @@ struct agp_bridge_data {
#define I810_PTE_MAIN_UNCACHED 0x00000000
#define I810_PTE_LOCAL 0x00000002
#define I810_PTE_VALID 0x00000001
#define I830_PTE_SYSTEM_CACHED 0x00000006
#define I810_SMRAM_MISCC 0x70
#define I810_GFX_MEM_WIN_SIZE 0x00010000
#define I810_GFX_MEM_WIN_32M 0x00010000
......@@ -270,8 +272,16 @@ void global_cache_flush(void);
void get_agp_version(struct agp_bridge_data *bridge);
unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge,
unsigned long addr, int type);
int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge,
int type);
struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev);
/* generic functions for user-populated AGP memory types */
struct agp_memory *agp_generic_alloc_user(size_t page_count, int type);
void agp_alloc_page_array(size_t size, struct agp_memory *mem);
void agp_free_page_array(struct agp_memory *mem);
/* generic routines for agp>=3 */
int agp3_generic_fetch_size(void);
void agp3_generic_tlbflush(struct agp_memory *mem);
......
......@@ -214,6 +214,7 @@ static struct agp_bridge_driver ali_generic_bridge = {
.free_by_type = agp_generic_free_by_type,
.agp_alloc_page = agp_generic_alloc_page,
.agp_destroy_page = ali_destroy_page,
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
};
static struct agp_bridge_driver ali_m1541_bridge = {
......@@ -237,6 +238,7 @@ static struct agp_bridge_driver ali_m1541_bridge = {
.free_by_type = agp_generic_free_by_type,
.agp_alloc_page = m1541_alloc_page,
.agp_destroy_page = m1541_destroy_page,
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
};
......
......@@ -91,6 +91,9 @@ static int alpha_core_agp_insert_memory(struct agp_memory *mem, off_t pg_start,
int num_entries, status;
void *temp;
if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES)
return -EINVAL;
temp = agp_bridge->current_size;
num_entries = A_SIZE_FIX(temp)->num_entries;
if ((pg_start + mem->page_count) > num_entries)
......@@ -142,6 +145,7 @@ struct agp_bridge_driver alpha_core_agp_driver = {
.free_by_type = agp_generic_free_by_type,
.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,
};
struct agp_bridge_data *alpha_bridge;
......
......@@ -381,6 +381,7 @@ static struct agp_bridge_driver amd_irongate_driver = {
.free_by_type = agp_generic_free_by_type,
.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,
};
static struct agp_device_ids amd_agp_device_ids[] __devinitdata =
......
......@@ -62,12 +62,18 @@ static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
{
int i, j, num_entries;
long long tmp;
int mask_type;
struct agp_bridge_data *bridge = mem->bridge;
u32 pte;
num_entries = agp_num_entries();
if (type != 0 || mem->type != 0)
if (type != mem->type)
return -EINVAL;
mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
if (mask_type != 0)
return -EINVAL;
/* Make sure we can fit the range in the gatt table. */
/* FIXME: could wrap */
......@@ -90,7 +96,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,
mem->memory[i], mem->type);
mem->memory[i], mask_type);
BUG_ON(tmp & 0xffffff0000000ffcULL);
pte = (tmp & 0x000000ff00000000ULL) >> 28;
......@@ -247,6 +253,7 @@ static struct agp_bridge_driver amd_8151_driver = {
.free_by_type = agp_generic_free_by_type,
.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,
};
/* Some basic sanity checks for the aperture. */
......
......@@ -431,6 +431,7 @@ static struct agp_bridge_driver ati_generic_bridge = {
.free_by_type = agp_generic_free_by_type,
.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,
};
......
......@@ -43,7 +43,7 @@
* fix some real stupidity. It's only by chance we can bump
* past 0.99 at all due to some boolean logic error. */
#define AGPGART_VERSION_MAJOR 0
#define AGPGART_VERSION_MINOR 101
#define AGPGART_VERSION_MINOR 102
static const struct agp_version agp_current_version =
{
.major = AGPGART_VERSION_MAJOR,
......
......@@ -335,6 +335,7 @@ static struct agp_bridge_driver efficeon_driver = {
.free_by_type = agp_generic_free_by_type,
.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,
};
static int __devinit agp_efficeon_probe(struct pci_dev *pdev,
......
......@@ -892,6 +892,9 @@ static int agpioc_allocate_wrap(struct agp_file_private *priv, void __user *arg)
if (copy_from_user(&alloc, arg, sizeof(struct agp_allocate)))
return -EFAULT;
if (alloc.type >= AGP_USER_TYPES)
return -EINVAL;
memory = agp_allocate_memory_wrap(alloc.pg_count, alloc.type);
if (memory == NULL)
......
......@@ -101,6 +101,67 @@ static int agp_get_key(void)
return -1;
}
/*
* Use kmalloc if possible for the page list. Otherwise fall back to
* vmalloc. This speeds things up and also saves memory for small AGP
* regions.
*/
void agp_alloc_page_array(size_t size, struct agp_memory *mem)
{
mem->memory = NULL;
mem->vmalloc_flag = 0;
if (size <= 2*PAGE_SIZE) {
mem->memory = kmalloc(size, GFP_KERNEL | __GFP_NORETRY);
}
if (mem->memory == NULL) {
mem->memory = vmalloc(size);
mem->vmalloc_flag = 1;
}
}
EXPORT_SYMBOL(agp_alloc_page_array);
void agp_free_page_array(struct agp_memory *mem)
{
if (mem->vmalloc_flag) {
vfree(mem->memory);
} else {
kfree(mem->memory);
}
}
EXPORT_SYMBOL(agp_free_page_array);
static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages)
{
struct agp_memory *new;
unsigned long alloc_size = num_agp_pages*sizeof(struct page *);
new = kmalloc(sizeof(struct agp_memory), GFP_KERNEL);
if (new == NULL)
return NULL;
memset(new, 0, sizeof(struct agp_memory));
new->key = agp_get_key();
if (new->key < 0) {
kfree(new);
return NULL;
}
agp_alloc_page_array(alloc_size, new);
if (new->memory == NULL) {
agp_free_key(new->key);
kfree(new);
return NULL;
}
new->num_scratch_pages = 0;
return new;
}
struct agp_memory *agp_create_memory(int scratch_pages)
{
......@@ -116,7 +177,8 @@ struct agp_memory *agp_create_memory(int scratch_pages)
kfree(new);
return NULL;
}
new->memory = vmalloc(PAGE_SIZE * scratch_pages);
agp_alloc_page_array(PAGE_SIZE * scratch_pages, new);
if (new->memory == NULL) {
agp_free_key(new->key);
......@@ -124,6 +186,7 @@ struct agp_memory *agp_create_memory(int scratch_pages)
return NULL;
}
new->num_scratch_pages = scratch_pages;
new->type = AGP_NORMAL_MEMORY;
return new;
}
EXPORT_SYMBOL(agp_create_memory);
......@@ -146,6 +209,11 @@ void agp_free_memory(struct agp_memory *curr)
if (curr->is_bound == TRUE)
agp_unbind_memory(curr);
if (curr->type >= AGP_USER_TYPES) {
agp_generic_free_by_type(curr);
return;
}
if (curr->type != 0) {
curr->bridge->driver->free_by_type(curr);
return;
......@@ -157,7 +225,7 @@ void agp_free_memory(struct agp_memory *curr)
flush_agp_mappings();
}
agp_free_key(curr->key);
vfree(curr->memory);
agp_free_page_array(curr);
kfree(curr);
}
EXPORT_SYMBOL(agp_free_memory);
......@@ -188,6 +256,13 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge,
if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp)
return NULL;
if (type >= AGP_USER_TYPES) {
new = agp_generic_alloc_user(page_count, type);
if (new)
new->bridge = bridge;
return new;
}
if (type != 0) {
new = bridge->driver->alloc_by_type(page_count, type);
if (new)
......@@ -960,6 +1035,7 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
off_t j;
void *temp;
struct agp_bridge_data *bridge;
int mask_type;
bridge = mem->bridge;
if (!bridge)
......@@ -995,7 +1071,12 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
num_entries -= agp_memory_reserved/PAGE_SIZE;
if (num_entries < 0) num_entries = 0;
if (type != 0 || mem->type != 0) {
if (type != mem->type) {
return -EINVAL;
}
mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
if (mask_type != 0) {
/* The generic routines know nothing of memory types */
return -EINVAL;
}
......@@ -1018,7 +1099,8 @@ 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++) {
writel(bridge->driver->mask_memory(bridge, mem->memory[i], mem->type), bridge->gatt_table+j);
writel(bridge->driver->mask_memory(bridge, mem->memory[i], mask_type),
bridge->gatt_table+j);
}
readl(bridge->gatt_table+j-1); /* PCI Posting. */
......@@ -1032,6 +1114,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
{
size_t i;
struct agp_bridge_data *bridge;
int mask_type;
bridge = mem->bridge;
if (!bridge)
......@@ -1040,7 +1123,11 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
if (mem->page_count == 0)
return 0;
if (type != 0 || mem->type != 0) {
if (type != mem->type)
return -EINVAL;
mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
if (mask_type != 0) {
/* The generic routines know nothing of memory types */
return -EINVAL;
}
......@@ -1066,12 +1153,34 @@ EXPORT_SYMBOL(agp_generic_alloc_by_type);
void agp_generic_free_by_type(struct agp_memory *curr)
{
vfree(curr->memory);
agp_free_page_array(curr);
agp_free_key(curr->key);
kfree(curr);
}
EXPORT_SYMBOL(agp_generic_free_by_type);
struct agp_memory *agp_generic_alloc_user(size_t page_count, int type)
{
struct agp_memory *new;
int i;
int pages;
pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
new = agp_create_user_memory(page_count);
if (new == NULL)
return NULL;
for (i = 0; i < page_count; i++) {
new->memory[i] = 0;
}
new->page_count = 0;
new->type = type;
new->num_scratch_pages = pages;
return new;
}
EXPORT_SYMBOL(agp_generic_alloc_user);
/*
* Basic Page Allocation Routines -
......@@ -1165,6 +1274,15 @@ unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge,
}
EXPORT_SYMBOL(agp_generic_mask_memory);
int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge,
int type)
{
if (type >= AGP_USER_TYPES)
return 0;
return type;
}
EXPORT_SYMBOL(agp_generic_type_to_mask_type);
/*
* These functions are implemented according to the AGPv3 spec,
* which covers implementation details that had previously been
......
......@@ -438,6 +438,7 @@ struct agp_bridge_driver hp_zx1_driver = {
.free_by_type = agp_generic_free_by_type,
.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 = 1,
};
......
......@@ -293,6 +293,9 @@ static int i460_insert_memory_small_io_page (struct agp_memory *mem,
pr_debug("i460_insert_memory_small_io_page(mem=%p, pg_start=%ld, type=%d, paddr0=0x%lx)\n",
mem, pg_start, type, mem->memory[0]);
if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES)
return -EINVAL;
io_pg_start = I460_IOPAGES_PER_KPAGE * pg_start;
temp = agp_bridge->current_size;
......@@ -396,6 +399,9 @@ static int i460_insert_memory_large_io_page (struct agp_memory *mem,
struct lp_desc *start, *end, *lp;
void *temp;
if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES)
return -EINVAL;
temp = agp_bridge->current_size;
num_entries = A_SIZE_8(temp)->num_entries;
......@@ -572,6 +578,7 @@ struct agp_bridge_driver intel_i460_driver = {
#endif
.alloc_by_type = agp_generic_alloc_by_type,
.free_by_type = agp_generic_free_by_type,
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
.cant_use_aperture = 1,
};
......
This diff is collapsed.
......@@ -310,6 +310,7 @@ static struct agp_bridge_driver nvidia_driver = {
.free_by_type = agp_generic_free_by_type,
.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,
};
static int __devinit agp_nvidia_probe(struct pci_dev *pdev,
......
......@@ -265,6 +265,7 @@ struct agp_bridge_driver sgi_tioca_driver = {
.free_by_type = agp_generic_free_by_type,
.agp_alloc_page = sgi_tioca_alloc_page,
.agp_destroy_page = agp_generic_destroy_page,
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
.cant_use_aperture = 1,
.needs_scratch_page = 0,
.num_aperture_sizes = 1,
......
......@@ -444,6 +444,7 @@ static struct agp_bridge_driver sworks_driver = {
.free_by_type = agp_generic_free_by_type,
.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,
};
static int __devinit agp_serverworks_probe(struct pci_dev *pdev,
......
......@@ -510,6 +510,7 @@ struct agp_bridge_driver uninorth_agp_driver = {
.free_by_type = agp_generic_free_by_type,
.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 = 1,
};
......@@ -534,6 +535,7 @@ struct agp_bridge_driver u3_agp_driver = {
.free_by_type = agp_generic_free_by_type,
.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 = 1,
.needs_scratch_page = 1,
};
......
......@@ -191,6 +191,7 @@ static struct agp_bridge_driver via_agp3_driver = {
.free_by_type = agp_generic_free_by_type,
.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,
};
static struct agp_bridge_driver via_driver = {
......@@ -214,6 +215,7 @@ static struct agp_bridge_driver via_driver = {
.free_by_type = agp_generic_free_by_type,
.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,
};
static struct agp_device_ids via_agp_device_ids[] __devinitdata =
......
......@@ -87,10 +87,15 @@ struct agp_memory {
u32 physical;
u8 is_bound;
u8 is_flushed;
u8 vmalloc_flag;
};
#define AGP_NORMAL_MEMORY 0
#define AGP_USER_TYPES (1 << 16)
#define AGP_USER_MEMORY (AGP_USER_TYPES)
#define AGP_USER_CACHED_MEMORY (AGP_USER_TYPES + 1)
extern struct agp_bridge_data *agp_bridge;
extern struct list_head agp_bridges;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment