Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a1bd924 authored by Thomas Hellstrom's avatar Thomas Hellstrom Committed by Dave Airlie
Browse files

drm: add simple DRM memory manager, and hash table



This adds the DRM hashtable and simple memory manager implementations from
Tungsten Graphics, this is NOT the new memory manager, this is a replacement
for the SIS and VIA memory managers.

Signed-off-by: default avatarDave Airlie <airlied@linux.ie>
parent b9b603dd
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ 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_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \
		drm_sysfs.o
		drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o

tdfx-objs   := tdfx_drv.o
r128-objs   := r128_drv.o r128_cce.o r128_state.o r128_irq.o
+32 −1
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@
#define __OS_HAS_MTRR (defined(CONFIG_MTRR))

#include "drm_os_linux.h"
#include "drm_hashtab.h"

/***********************************************************************/
/** \name DRM template customization defaults */
@@ -135,6 +136,8 @@
#define DRM_MEM_STUB      19
#define DRM_MEM_SGLISTS   20
#define DRM_MEM_CTXLIST   21
#define DRM_MEM_MM        22
#define DRM_MEM_HASHTAB   23

#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8)

@@ -515,6 +518,22 @@ 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
@@ -1001,6 +1020,18 @@ extern struct class_device *drm_sysfs_device_add(struct 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)
+190 −0
Original line number Diff line number Diff line
/**************************************************************************
 *
 * 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 Hellstrm <thomas-at-tungstengraphics-dot-com>
 */

#include "drmP.h"
#include "drm_hashtab.h"
#include <linux/hash.h>

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 shift,
			      unsigned long add)
{
	int ret;
	unsigned long mask = (1 << bits) - 1;
	unsigned long first, unshifted_key;

	unshifted_key = hash_long(seed, bits);
	first = unshifted_key;
	do {
		item->key = (unshifted_key << shift) + add;
		ret = drm_ht_insert_item(ht, item);
		if (ret)
			unshifted_key = (unshifted_key + 1) & mask;
	} while(ret && (unshifted_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;
	}
}
+67 −0
Original line number Diff line number Diff line
/**************************************************************************
 *
 * 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 <thomas-at-tungstengraphics-dot-com>
 */

#ifndef DRM_HASHTAB_H
#define DRM_HASHTAB_H

#define drm_hash_entry(_ptr, _type, _member) container_of(_ptr, _type, _member)

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, int shift,
				     unsigned long add);
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
+201 −0
Original line number Diff line number Diff line
/**************************************************************************
 *
 * 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 Hellstrm <thomas-at-tungstengraphics-dot-com>
 */

#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);
Loading