From 6bacb180cef00573fc41a1e79bdd5b89d6f1c1f5 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 6 Jun 2006 14:19:00 +0000 Subject: Merge in the drm-sman-branch --- linux-core/Makefile | 14 +- linux-core/Makefile.kernel | 9 +- linux-core/drmP.h | 35 ++++- linux-core/drm_hashtab.c | 194 +++++++++++++++++++++++++ linux-core/drm_hashtab.h | 64 +++++++++ linux-core/drm_mm.c | 201 ++++++++++++++++++++++++++ linux-core/drm_sman.c | 352 +++++++++++++++++++++++++++++++++++++++++++++ linux-core/drm_sman.h | 176 +++++++++++++++++++++++ linux-core/sis_drv.c | 36 ++++- linux-core/sis_mm.c | 286 ++++++++++++++++++++++++++++++++++++ linux-core/via_mm.c | 203 ++++++++++++++++++++++++++ 11 files changed, 1555 insertions(+), 15 deletions(-) create mode 100644 linux-core/drm_hashtab.c create mode 100644 linux-core/drm_hashtab.h create mode 100644 linux-core/drm_mm.c create mode 100644 linux-core/drm_sman.c create mode 100644 linux-core/drm_sman.h create mode 100644 linux-core/sis_mm.c create mode 100644 linux-core/via_mm.c (limited to 'linux-core') diff --git a/linux-core/Makefile b/linux-core/Makefile index 9bb578cd..32828d28 100644 --- a/linux-core/Makefile +++ b/linux-core/Makefile @@ -77,7 +77,7 @@ DRM_MODULES ?= $(MODULE_LIST) DRMSHARED = drm.h drm_sarea.h DRMHEADERS = drmP.h drm_compat.h drm_os_linux.h $(DRMSHARED) -COREHEADERS = drm_core.h +COREHEADERS = drm_core.h drm_sman.h drm_hashtab.h TDFXHEADERS = tdfx_drv.h $(DRMHEADERS) TDFXSHARED = tdfx_drv.h @@ -93,15 +93,13 @@ I810HEADERS = i810_drv.h i810_drm.h $(DRMHEADERS) I830HEADERS = i830_drv.h i830_drm.h $(DRMHEADERS) I915HEADERS = i915_drv.h i915_drm.h $(DRMHEADERS) I915SHARED = i915_drv.h i915_drm.h i915_irq.c i915_mem.c i915_dma.c -SISHEADERS= sis_drv.h sis_drm.h $(DRMHEADERS) -SISSHARED= sis_drv.h sis_drm.h sis_ds.c sis_ds.h sis_mm.c +SISHEADERS= sis_drv.h sis_drm.h drm_hashtab.h drm_sman.h $(DRMHEADERS) +SISSHARED= sis_drv.h sis_drm.h SAVAGEHEADERS= savage_drv.h savage_drm.h $(DRMHEADERS) SAVAGESHARED= savage_drv.h savage_drm.h savage_bci.c savage_state.c -VIAHEADERS = via_drm.h via_drv.h via_mm.h via_ds.h \ - via_3d_reg.h via_verifier.h $(DRMHEADERS) -VIASHARED = via_drm.h via_drv.h via_mm.h via_ds.h \ - via_3d_reg.h via_drv.c via_ds.c via_irq.c via_map.c \ - via_mm.c via_dma.c via_verifier.c via_verifier.h via_video.c +VIAHEADERS = via_drm.h via_drv.h via_3d_reg.h via_verifier.h $(DRMHEADERS) +VIASHARED = via_drm.h via_drv.h via_3d_reg.h via_drv.c via_irq.c via_map.c \ + via_dma.c via_verifier.c via_verifier.h via_video.c MACH64HEADERS = mach64_drv.h mach64_drm.h $(DRMHEADERS) MACH64SHARED = mach64_drv.h mach64_drm.h mach64_dma.c \ mach64_irq.c mach64_state.c diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index d63aabb6..211e5b05 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -11,7 +11,8 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \ drm_drv.o drm_fops.o drm_ioctl.o drm_irq.o \ drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \ drm_sysfs.o drm_pci.o drm_agpsupport.o drm_scatter.o \ - drm_memory_debug.o ati_pcigart.o + drm_memory_debug.o ati_pcigart.o drm_sman.o \ + drm_hashtab.o drm_mm.o tdfx-objs := tdfx_drv.o r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o @@ -19,11 +20,11 @@ i810-objs := i810_drv.o i810_dma.o i830-objs := i830_drv.o i830_dma.o i830_irq.o i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o -sis-objs := sis_drv.o sis_ds.o sis_mm.o +sis-objs := sis_drv.o sis_mm.o ffb-objs := ffb_drv.o ffb_context.o savage-objs := savage_drv.o savage_bci.o savage_state.o -via-objs := via_irq.o via_drv.o via_ds.o via_map.o via_mm.o via_dma.o via_verifier.o \ - via_video.o via_dmablit.o +via-objs := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o \ + via_video.o via_dmablit.o mach64-objs := mach64_drv.o mach64_dma.o mach64_irq.o mach64_state.o nv-objs := nv_drv.o diff --git a/linux-core/drmP.h b/linux-core/drmP.h index b2295b99..ec72ebca 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -147,7 +147,9 @@ #define DRM_MEM_CTXBITMAP 18 #define DRM_MEM_STUB 19 #define DRM_MEM_SGLISTS 20 -#define DRM_MEM_CTXLIST 21 +#define DRM_MEM_CTXLIST 21 +#define DRM_MEM_MM 22 +#define DRM_MEM_HASHTAB 23 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) @@ -533,6 +535,24 @@ typedef struct ati_pcigart_info { drm_local_map_t mapping; } drm_ati_pcigart_info; +/* + * Generic memory manager structs + */ + +typedef struct drm_mm_node { + struct list_head fl_entry; + struct list_head ml_entry; + int free; + unsigned long start; + unsigned long size; + void *private; +} drm_mm_node_t; + +typedef struct drm_mm { + drm_mm_node_t root_node; +} drm_mm_t; + + /** * DRM driver structure. This structure represent the common code for * a family of cards. There will one drm_device for each card present @@ -1017,6 +1037,19 @@ extern struct class_device *drm_sysfs_device_add(struct drm_sysfs_class *cs, drm_head_t * head); extern void drm_sysfs_device_remove(struct class_device *class_dev); +/* + * Basic memory manager support (drm_mm.c) + */ + +extern drm_mm_node_t * drm_mm_get_block(drm_mm_node_t * parent, unsigned long size, + unsigned alignment); +extern void drm_mm_put_block(drm_mm_t *mm, drm_mm_node_t *cur); +extern drm_mm_node_t *drm_mm_search_free(const drm_mm_t *mm, unsigned long size, + unsigned alignment, int best_match); +extern int drm_mm_init(drm_mm_t *mm, unsigned long start, unsigned long size); +extern void drm_mm_takedown(drm_mm_t *mm); + + /* Inline replacements for DRM_IOREMAP macros */ static __inline__ void drm_core_ioremap(struct drm_map *map, struct drm_device *dev) diff --git a/linux-core/drm_hashtab.c b/linux-core/drm_hashtab.c new file mode 100644 index 00000000..3be781df --- /dev/null +++ b/linux-core/drm_hashtab.c @@ -0,0 +1,194 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ +/* + * Simple open hash tab implementation. + * + * Authors: + * Thomas Hellström + */ + +#include "drmP.h" +#include "drm_hashtab.h" +#include + +int +drm_ht_create(drm_open_hash_t *ht, unsigned int order) +{ + unsigned int i; + + ht->size = 1 << order; + ht->order = order; + ht->fill = 0; + ht->table = vmalloc(ht->size*sizeof(*ht->table)); + if (!ht->table) { + DRM_ERROR("Out of memory for hash table\n"); + return -ENOMEM; + } + for (i=0; i< ht->size; ++i) { + INIT_HLIST_HEAD(&ht->table[i]); + } + return 0; +} + +void +drm_ht_verbose_list(drm_open_hash_t *ht, unsigned long key) +{ + drm_hash_item_t *entry; + struct hlist_head *h_list; + struct hlist_node *list; + unsigned int hashed_key; + int count = 0; + + hashed_key = hash_long(key, ht->order); + DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key); + h_list = &ht->table[hashed_key]; + hlist_for_each(list, h_list) { + entry = hlist_entry(list, drm_hash_item_t, head); + DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); + } +} + +static struct hlist_node +*drm_ht_find_key(drm_open_hash_t *ht, unsigned long key) +{ + drm_hash_item_t *entry; + struct hlist_head *h_list; + struct hlist_node *list; + unsigned int hashed_key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + hlist_for_each(list, h_list) { + entry = hlist_entry(list, drm_hash_item_t, head); + if (entry->key == key) + return list; + if (entry->key > key) + break; + } + return NULL; +} + + +int +drm_ht_insert_item(drm_open_hash_t *ht, drm_hash_item_t *item) +{ + drm_hash_item_t *entry; + struct hlist_head *h_list; + struct hlist_node *list, *parent; + unsigned int hashed_key; + unsigned long key = item->key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + parent = NULL; + hlist_for_each(list, h_list) { + entry = hlist_entry(list, drm_hash_item_t, head); + if (entry->key == key) + return -1; + if (entry->key > key) + break; + parent = list; + } + if (parent) { + hlist_add_after(parent, &item->head); + } else { + hlist_add_head(&item->head, h_list); + } + return 0; +} + +/* + * Just insert an item and return any "bits" bit key that hasn't been used before. + */ + +int +drm_ht_just_insert_please(drm_open_hash_t *ht, drm_hash_item_t *item, + unsigned long seed, int bits) +{ + int ret; + unsigned long mask = (1 << bits) - 1; + unsigned long first; + + item->key = hash_long(seed, bits); + first = item->key; + do{ + ret = drm_ht_insert_item(ht, item); + if (ret) + item->key = (item->key + 1) & mask; + } while(ret && (item->key != first)); + + if (ret) { + DRM_ERROR("Available key bit space exhausted\n"); + return -EINVAL; + } + return 0; +} + +int +drm_ht_find_item(drm_open_hash_t *ht, unsigned long key, drm_hash_item_t **item) +{ + struct hlist_node *list; + + list = drm_ht_find_key(ht, key); + if (!list) + return -1; + + *item = hlist_entry(list, drm_hash_item_t, head); + return 0; +} + +int +drm_ht_remove_key(drm_open_hash_t *ht, unsigned long key) +{ + struct hlist_node *list; + + list = drm_ht_find_key(ht, key); + if (list) { + hlist_del_init(list); + ht->fill--; + return 0; + } + return -1; +} + +int +drm_ht_remove_item(drm_open_hash_t *ht, drm_hash_item_t *item) +{ + hlist_del_init(&item->head); + ht->fill--; + return 0; +} + +void drm_ht_remove(drm_open_hash_t *ht) +{ + if (ht->table) { + vfree(ht->table); + ht->table = NULL; + } +} + diff --git a/linux-core/drm_hashtab.h b/linux-core/drm_hashtab.h new file mode 100644 index 00000000..7f457e74 --- /dev/null +++ b/linux-core/drm_hashtab.h @@ -0,0 +1,64 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismack, ND. USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ +/* + * Simple open hash tab implementation. + * + * Authors: + * Thomas Hellström + */ + +#ifndef DRM_HASHTAB_H +#define DRM_HASHTAB_H + +typedef struct drm_hash_item{ + struct hlist_node head; + unsigned long key; +} drm_hash_item_t; + +typedef struct drm_open_hash{ + unsigned int size; + unsigned int order; + unsigned int fill; + struct hlist_head *table; +} drm_open_hash_t; + + +extern int drm_ht_create(drm_open_hash_t *ht, unsigned int order); +extern int drm_ht_insert_item(drm_open_hash_t *ht, drm_hash_item_t *item); +extern int drm_ht_just_insert_please(drm_open_hash_t *ht, drm_hash_item_t *item, + unsigned long seed, int bits); +extern int drm_ht_find_item(drm_open_hash_t *ht, unsigned long key, drm_hash_item_t **item); + +extern void drm_ht_verbose_list(drm_open_hash_t *ht, unsigned long key); +extern int drm_ht_remove_key(drm_open_hash_t *ht, unsigned long key); +extern int drm_ht_remove_item(drm_open_hash_t *ht, drm_hash_item_t *item); +extern void drm_ht_remove(drm_open_hash_t *ht); + + +#endif + diff --git a/linux-core/drm_mm.c b/linux-core/drm_mm.c new file mode 100644 index 00000000..a93760f9 --- /dev/null +++ b/linux-core/drm_mm.c @@ -0,0 +1,201 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ + +/* + * Generic simple memory manager implementation. Intended to be used as a base + * class implementation for more advanced memory managers. + * + * Note that the algorithm used is quite simple and there might be substantial + * performance gains if a smarter free list is implemented. Currently it is just an + * unordered stack of free regions. This could easily be improved if an RB-tree + * is used instead. At least if we expect heavy fragmentation. + * + * Aligned allocations can also see improvement. + * + * Authors: + * Thomas Hellström + */ + +#include "drmP.h" + +drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent, + unsigned long size, unsigned alignment) +{ + + drm_mm_node_t *child; + + if (alignment) + size += alignment - 1; + + if (parent->size == size) { + list_del_init(&parent->fl_entry); + parent->free = FALSE; + return parent; + } else { + child = (drm_mm_node_t *) drm_alloc(sizeof(*child), DRM_MEM_MM); + if (!child) + return NULL; + + INIT_LIST_HEAD(&child->ml_entry); + INIT_LIST_HEAD(&child->fl_entry); + + child->free = FALSE; + child->size = size; + child->start = parent->start; + + list_add_tail(&child->ml_entry, &parent->ml_entry); + parent->size -= size; + parent->start += size; + } + return child; +} + +/* + * Put a block. Merge with the previous and / or next block if they are free. + * Otherwise add to the free stack. + */ + +void drm_mm_put_block(drm_mm_t * mm, drm_mm_node_t * cur) +{ + + drm_mm_node_t *list_root = &mm->root_node; + struct list_head *cur_head = &cur->ml_entry; + struct list_head *root_head = &list_root->ml_entry; + drm_mm_node_t *prev_node = NULL; + drm_mm_node_t *next_node; + + int merged = FALSE; + + if (cur_head->prev != root_head) { + prev_node = list_entry(cur_head->prev, drm_mm_node_t, ml_entry); + if (prev_node->free) { + prev_node->size += cur->size; + merged = TRUE; + } + } + if (cur_head->next != root_head) { + next_node = list_entry(cur_head->next, drm_mm_node_t, ml_entry); + if (next_node->free) { + if (merged) { + prev_node->size += next_node->size; + list_del(&next_node->ml_entry); + list_del(&next_node->fl_entry); + drm_free(next_node, sizeof(*next_node), + DRM_MEM_MM); + } else { + next_node->size += cur->size; + next_node->start = cur->start; + merged = TRUE; + } + } + } + if (!merged) { + cur->free = TRUE; + list_add(&cur->fl_entry, &list_root->fl_entry); + } else { + list_del(&cur->ml_entry); + drm_free(cur, sizeof(*cur), DRM_MEM_MM); + } +} + +drm_mm_node_t *drm_mm_search_free(const drm_mm_t * mm, + unsigned long size, + unsigned alignment, int best_match) +{ + struct list_head *list; + const struct list_head *free_stack = &mm->root_node.fl_entry; + drm_mm_node_t *entry; + drm_mm_node_t *best; + unsigned long best_size; + + best = NULL; + best_size = ~0UL; + + if (alignment) + size += alignment - 1; + + list_for_each(list, free_stack) { + entry = list_entry(list, drm_mm_node_t, fl_entry); + if (entry->size >= size) { + if (!best_match) + return entry; + if (size < best_size) { + best = entry; + best_size = entry->size; + } + } + } + + return best; +} + +int drm_mm_init(drm_mm_t * mm, unsigned long start, unsigned long size) +{ + drm_mm_node_t *child; + + INIT_LIST_HEAD(&mm->root_node.ml_entry); + INIT_LIST_HEAD(&mm->root_node.fl_entry); + child = (drm_mm_node_t *) drm_alloc(sizeof(*child), DRM_MEM_MM); + if (!child) + return -ENOMEM; + + INIT_LIST_HEAD(&child->ml_entry); + INIT_LIST_HEAD(&child->fl_entry); + + child->start = start; + child->size = size; + child->free = TRUE; + + list_add(&child->fl_entry, &mm->root_node.fl_entry); + list_add(&child->ml_entry, &mm->root_node.ml_entry); + + return 0; +} + +EXPORT_SYMBOL(drm_mm_init); + +void drm_mm_takedown(drm_mm_t * mm) +{ + struct list_head *bnode = mm->root_node.fl_entry.next; + drm_mm_node_t *entry; + + entry = list_entry(bnode, drm_mm_node_t, fl_entry); + + if (entry->ml_entry.next != &mm->root_node.ml_entry || + entry->fl_entry.next != &mm->root_node.fl_entry) { + DRM_ERROR("Memory manager not clean. Delaying takedown\n"); + return; + } + + list_del(&entry->fl_entry); + list_del(&entry->ml_entry); + + drm_free(entry, sizeof(*entry), DRM_MEM_MM); +} + +EXPORT_SYMBOL(drm_mm_takedown); diff --git a/linux-core/drm_sman.c b/linux-core/drm_sman.c new file mode 100644 index 00000000..96557932 --- /dev/null +++ b/linux-core/drm_sman.c @@ -0,0 +1,352 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * + **************************************************************************/ +/* + * Simple memory manager interface that keeps track on allocate regions on a + * per "owner" basis. All regions associated with an "owner" can be released + * with a simple call. Typically if the "owner" exists. The owner is any + * "unsigned long" identifier. Can typically be a pointer to a file private + * struct or a context identifier. + * + * Authors: + * Thomas Hellström + */ + +#include "drm_sman.h" + +typedef struct drm_owner_item { + drm_hash_item_t owner_hash; + struct list_head sman_list; + struct list_head mem_blocks; +} drm_owner_item_t; + +void drm_sman_takedown(drm_sman_t * sman) +{ + drm_ht_remove(&sman->user_hash_tab); + drm_ht_remove(&sman->owner_hash_tab); + if (sman->mm) + drm_free(sman->mm, sman->num_managers * sizeof(*sman->mm), + DRM_MEM_MM); +} + +EXPORT_SYMBOL(drm_sman_takedown); + +int +drm_sman_init(drm_sman_t * sman, unsigned int num_managers, + unsigned int user_order, unsigned int owner_order) +{ + int ret = 0; + + sman->mm = (drm_sman_mm_t *) drm_calloc(num_managers, sizeof(*sman->mm), + DRM_MEM_MM); + if (!sman->mm) { + ret = -ENOMEM; + goto out; + } + sman->num_managers = num_managers; + INIT_LIST_HEAD(&sman->owner_items); + ret = drm_ht_create(&sman->owner_hash_tab, owner_order); + if (ret) + goto out1; + ret = drm_ht_create(&sman->user_hash_tab, user_order); + if (!ret) + goto out; + + drm_ht_remove(&sman->owner_hash_tab); + out1: + drm_free(sman->mm, num_managers * sizeof(*sman->mm), DRM_MEM_MM); + out: + return ret; +} + +EXPORT_SYMBOL(drm_sman_init); + +static void *drm_sman_mm_allocate(void *private, unsigned long size, + unsigned alignment) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_node_t *tmp; + + tmp = drm_mm_search_free(mm, size, alignment, TRUE); + if (!tmp) { + return NULL; + } + tmp = drm_mm_get_block(tmp, size, alignment); + return tmp; +} + +static void drm_sman_mm_free(void *private, void *ref) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_node_t *node = (drm_mm_node_t *) ref; + + drm_mm_put_block(mm, node); +} + +static void drm_sman_mm_destroy(void *private) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_takedown(mm); + drm_free(mm, sizeof(*mm), DRM_MEM_MM); +} + +unsigned long drm_sman_mm_offset(void *private, void *ref) +{ + drm_mm_node_t *node = (drm_mm_node_t *) ref; + return node->start; +} + +int +drm_sman_set_range(drm_sman_t * sman, unsigned int manager, + unsigned long start, unsigned long size) +{ + drm_sman_mm_t *sman_mm; + drm_mm_t *mm; + int ret; + + BUG_ON(manager >= sman->num_managers); + + sman_mm = &sman->mm[manager]; + mm = drm_calloc(1, sizeof(*mm), DRM_MEM_MM); + if (!mm) { + return -ENOMEM; + } + sman_mm->private = mm; + ret = drm_mm_init(mm, start, size); + + if (ret) { + drm_free(mm, sizeof(*mm), DRM_MEM_MM); + return ret; + } + + sman_mm->allocate = drm_sman_mm_allocate; + sman_mm->free = drm_sman_mm_free; + sman_mm->destroy = drm_sman_mm_destroy; + sman_mm->offset = drm_sman_mm_offset; + + return 0; +} + +EXPORT_SYMBOL(drm_sman_set_range); + +int +drm_sman_set_manager(drm_sman_t * sman, unsigned int manager, + drm_sman_mm_t * allocator) +{ + BUG_ON(manager >= sman->num_managers); + sman->mm[manager] = *allocator; + + return 0; +} + +static drm_owner_item_t + * drm_sman_get_owner_item(drm_sman_t * sman, unsigned long owner) +{ + int ret; + drm_hash_item_t *owner_hash_item; + drm_owner_item_t *owner_item; + + ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item); + if (!ret) { + return list_entry(owner_hash_item, drm_owner_item_t, + owner_hash); + } + + owner_item = drm_calloc(1, sizeof(*owner_item), DRM_MEM_MM); + if (!owner_item) + goto out; + + INIT_LIST_HEAD(&owner_item->mem_blocks); + owner_item->owner_hash.key = owner; + if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash)) + goto out1; + + list_add_tail(&owner_item->sman_list, &sman->owner_items); + return owner_item; + + out1: + drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM); + out: + return NULL; +} + +drm_memblock_item_t *drm_sman_alloc(drm_sman_t * sman, unsigned int manager, + unsigned long size, unsigned alignment, + unsigned long owner) +{ + void *tmp; + drm_sman_mm_t *sman_mm; + drm_owner_item_t *owner_item; + drm_memblock_item_t *memblock; + + BUG_ON(manager >= sman->num_managers); + + sman_mm = &sman->mm[manager]; + tmp = sman_mm->allocate(sman_mm->private, size, alignment); + + if (!tmp) { + return NULL; + } + + memblock = drm_calloc(1, sizeof(*memblock), DRM_MEM_MM); + + if (!memblock) + goto out; + + memblock->mm_info = tmp; + memblock->mm = sman_mm; + memblock->sman = sman; + + if (drm_ht_just_insert_please + (&sman->user_hash_tab, &memblock->user_hash, + (unsigned long)memblock, 32)) + goto out1; + + owner_item = drm_sman_get_owner_item(sman, owner); + if (!owner_item) + goto out2; + + list_add_tail(&memblock->owner_list, &owner_item->mem_blocks); + + return memblock; + + out2: + drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash); + out1: + drm_free(memblock, sizeof(*memblock), DRM_MEM_MM); + out: + sman_mm->free(sman_mm->private, tmp); + + return NULL; +} + +EXPORT_SYMBOL(drm_sman_alloc); + +static void drm_sman_free(drm_memblock_item_t * item) +{ + drm_sman_t *sman = item->sman; + + list_del(&item->owner_list); + drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash); + item->mm->free(item->mm->private, item->mm_info); + drm_free(item, sizeof(*item), DRM_MEM_MM); +} + +int drm_sman_free_key(drm_sman_t * sman, unsigned int key) +{ + drm_hash_item_t *hash_item; + drm_memblock_item_t *memblock_item; + + if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item)) + return -EINVAL; + + memblock_item = list_entry(hash_item, drm_memblock_item_t, user_hash); + drm_sman_free(memblock_item); + return 0; +} + +EXPORT_SYMBOL(drm_sman_free_key); + +static void +drm_sman_remove_owner(drm_sman_t * sman, drm_owner_item_t * owner_item) +{ + list_del(&owner_item->sman_list); + drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash); + drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM); +} + +int drm_sman_owner_clean(drm_sman_t * sman, unsigned long owner) +{ + + drm_hash_item_t *hash_item; + drm_owner_item_t *owner_item; + + if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) { + return -1; + } + + owner_item = list_entry(hash_item, drm_owner_item_t, owner_hash); + if (owner_item->mem_blocks.next == &owner_item->mem_blocks) { + drm_sman_remove_owner(sman, owner_item); + return -1; + } + + return 0; +} + +EXPORT_SYMBOL(drm_sman_owner_clean); + +static void +drm_sman_do_owner_cleanup(drm_sman_t * sman, drm_owner_item_t * owner_item) +{ + drm_memblock_item_t *entry, *next; + + list_for_each_entry_safe(entry, next, &owner_item->mem_blocks, + owner_list) { + drm_sman_free(entry); + } + drm_sman_remove_owner(sman, owner_item); +} + +void drm_sman_owner_cleanup(drm_sman_t * sman, unsigned long owner) +{ + + drm_hash_item_t *hash_item; + drm_owner_item_t *owner_item; + + if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) { + + return; + } + + owner_item = list_entry(hash_item, drm_owner_item_t, owner_hash); + drm_sman_do_owner_cleanup(sman, owner_item); +} + +EXPORT_SYMBOL(drm_sman_owner_cleanup); + +void drm_sman_cleanup(drm_sman_t * sman) +{ + drm_owner_item_t *entry, *next; + unsigned int i; + drm_sman_mm_t *sman_mm; + + list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) { + drm_sman_do_owner_cleanup(sman, entry); + } + if (sman->mm) { + for (i = 0; i < sman->num_managers; ++i) { + sman_mm = &sman->mm[i]; + if (sman_mm->private) { + sman_mm->destroy(sman_mm->private); + sman_mm->private = NULL; + } + } + } +} + +EXPORT_SYMBOL(drm_sman_cleanup); diff --git a/linux-core/drm_sman.h b/linux-core/drm_sman.h new file mode 100644 index 00000000..7e537d94 --- /dev/null +++ b/linux-core/drm_sman.h @@ -0,0 +1,176 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ +/* + * Simple memory MANager interface that keeps track on allocate regions on a + * per "owner" basis. All regions associated with an "owner" can be released + * with a simple call. Typically if the "owner" exists. The owner is any + * "unsigned long" identifier. Can typically be a pointer to a file private + * struct or a context identifier. + * + * Authors: + * Thomas Hellström + */ + +#ifndef DRM_SMAN_H +#define DRM_SMAN_H + +#include "drmP.h" +#include "drm_hashtab.h" + +/* + * A class that is an abstration of a simple memory allocator. + * The sman implementation provides a default such allocator + * using the drm_mm.c implementation. But the user can replace it. + * See the SiS implementation, which may use the SiS FB kernel module + * for memory management. + */ + +typedef struct drm_sman_mm { + /* private info. If allocated, needs to be destroyed by the destroy + function */ + void *private; + + /* Allocate a memory block with given size and alignment. + Return an opaque reference to the memory block */ + + void *(*allocate) (void *private, unsigned long size, + unsigned alignment); + + /* Free a memory block. "ref" is the opaque reference that we got from + the "alloc" function */ + + void (*free) (void *private, void *ref); + + /* Free all resources associated with this allocator */ + + void (*destroy) (void *private); + + /* Return a memory offset from the opaque reference returned from the + "alloc" function */ + + unsigned long (*offset) (void *private, void *ref); +} drm_sman_mm_t; + +typedef struct drm_memblock_item { + struct list_head owner_list; + drm_hash_item_t user_hash; + void *mm_info; + drm_sman_mm_t *mm; + struct drm_sman *sman; +} drm_memblock_item_t; + +typedef struct drm_sman { + drm_sman_mm_t *mm; + int num_managers; + drm_open_hash_t owner_hash_tab; + drm_open_hash_t user_hash_tab; + struct list_head owner_items; +} drm_sman_t; + +/* + * Take down a memory manager. This function should only be called after a + * successful init and after a call to drm_sman_cleanup. + */ + +extern void drm_sman_takedown(drm_sman_t * sman); + +/* + * Allocate structures for a manager. + * num_managers are the number of memory pools to manage. (VRAM, AGP, ....) + * user_order is the log2 of the number of buckets in the user hash table. + * set this to approximately log2 of the max number of memory regions + * that will be allocated for _all_ pools together. + * owner_order is the log2 of the number of buckets in the owner hash table. + * set this to approximately log2 of + * the number of client file connections that will + * be using the manager. + * + */ + +extern int drm_sman_init(drm_sman_t * sman, unsigned int num_managers, + unsigned int user_order, unsigned int owner_order); + +/* + * Initialize a drm_mm.c allocator. Should be called only once for each + * manager unless a customized allogator is used. + */ + +extern int drm_sman_set_range(drm_sman_t * sman, unsigned int manager, + unsigned long start, unsigned long size); + +/* + * Initialize a customized allocator for one of the managers. + * (See the SiS module). The object pointed to by "allocator" is copied, + * so it can be destroyed after this call. + */ + +extern int drm_sman_set_manager(drm_sman_t * sman, unsigned int mananger, + drm_sman_mm_t * allocator); + +/* + * Allocate a memory block. Aligment is not implemented yet. + */ + +extern drm_memblock_item_t *drm_sman_alloc(drm_sman_t * sman, + unsigned int manager, + unsigned long size, + unsigned alignment, + unsigned long owner); +/* + * Free a memory block identified by its user hash key. + */ + +extern int drm_sman_free_key(drm_sman_t * sman, unsigned int key); + +/* + * returns TRUE iff there are no stale memory blocks associated with this owner. + * Typically called to determine if we need to idle the hardware and call + * drm_sman_owner_cleanup. If there are no stale memory blocks, it removes all + * resources associated with owner. + */ + +extern int drm_sman_owner_clean(drm_sman_t * sman, unsigned long owner); + +/* + * Frees all stale memory blocks associated with this owner. Note that this + * requires that the hardware is finished with all blocks, so the graphics engine + * should be idled before this call is made. This function also frees + * any resources associated with "owner" and should be called when owner + * is not going to be referenced anymore. + */ + +extern void drm_sman_owner_cleanup(drm_sman_t * sman, unsigned long owner); + +/* + * Frees all stale memory blocks associated with the memory manager. + * See idling above. + */ + +extern void drm_sman_cleanup(drm_sman_t * sman); + +#endif diff --git a/linux-core/sis_drv.c b/linux-core/sis_drv.c index 96c143a5..5d55357c 100644 --- a/linux-core/sis_drv.c +++ b/linux-core/sis_drv.c @@ -36,12 +36,44 @@ static struct pci_device_id pciidlist[] = { sis_PCI_IDS }; + +static int sis_driver_load(drm_device_t *dev, unsigned long chipset) +{ + drm_sis_private_t *dev_priv; + int ret; + + dev_priv = drm_calloc(1, sizeof(drm_sis_private_t), DRM_MEM_DRIVER); + if (dev_priv == NULL) + return DRM_ERR(ENOMEM); + + dev->dev_private = (void *)dev_priv; + dev_priv->chipset = chipset; + ret = drm_sman_init(&dev_priv->sman, 2, 12, 8); + if (ret) { + drm_free(dev_priv, sizeof(dev_priv), DRM_MEM_DRIVER); + } + + return ret; +} + +static int sis_driver_unload(drm_device_t *dev) +{ + drm_sis_private_t *dev_priv = dev->dev_private; + + drm_sman_takedown(&dev_priv->sman); + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + + return 0; +} + + static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR, - .context_ctor = sis_init_context, + .load = sis_driver_load, + .unload = sis_driver_unload, .context_dtor = sis_final_context, - .reclaim_buffers = drm_core_reclaim_buffers, + .reclaim_buffers = NULL, .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, .ioctls = sis_ioctls, diff --git a/linux-core/sis_mm.c b/linux-core/sis_mm.c new file mode 100644 index 00000000..ee924535 --- /dev/null +++ b/linux-core/sis_mm.c @@ -0,0 +1,286 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ + +/* + * Authors: + * Thomas Hellström + */ + +#include "drmP.h" +#include "sis_drm.h" +#include "sis_drv.h" + +#if defined(__linux__) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#include