Home | History | Annotate | Line # | Download | only in bsd-core
      1 /**************************************************************************
      2  *
      3  * Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., USA.
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
     17  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
     18  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
     19  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
     20  * USE OR OTHER DEALINGS IN THE SOFTWARE.
     21  *
     22  * The above copyright notice and this permission notice (including the
     23  * next paragraph) shall be included in all copies or substantial portions
     24  * of the Software.
     25  *
     26  *
     27  **************************************************************************/
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: drm_sman.c,v 1.1 2011/02/18 14:26:09 jmcneill Exp $");
     31 
     32 /*
     33  * Simple memory manager interface that keeps track on allocate regions on a
     34  * per "owner" basis. All regions associated with an "owner" can be released
     35  * with a simple call. Typically if the "owner" exists. The owner is any
     36  * "unsigned long" identifier. Can typically be a pointer to a file private
     37  * struct or a context identifier.
     38  *
     39  * Authors:
     40  * Thomas Hellstrm <thomas-at-tungstengraphics-dot-com>
     41  */
     42 
     43 #include "drmP.h"
     44 #include "drm_sman.h"
     45 
     46 struct drm_owner_item {
     47 	struct drm_hash_item owner_hash;
     48 	struct list_head sman_list;
     49 	struct list_head mem_blocks;
     50 };
     51 
     52 void drm_sman_takedown(struct drm_sman * sman)
     53 {
     54 	drm_ht_remove(&sman->user_hash_tab);
     55 	drm_ht_remove(&sman->owner_hash_tab);
     56 	if (sman->mm)
     57 		drm_free(sman->mm, sman->num_managers * sizeof(*sman->mm),
     58 		    DRM_MEM_MM);
     59 }
     60 
     61 int
     62 drm_sman_init(struct drm_sman * sman, unsigned int num_managers,
     63 	      unsigned int user_order, unsigned int owner_order)
     64 {
     65 	int ret = 0;
     66 
     67 	sman->mm = (struct drm_sman_mm *) drm_calloc(num_managers,
     68 	    sizeof(*sman->mm), DRM_MEM_MM);
     69 	if (!sman->mm) {
     70 		ret = -ENOMEM;
     71 		goto out;
     72 	}
     73 	sman->num_managers = num_managers;
     74 	INIT_LIST_HEAD(&sman->owner_items);
     75 	ret = drm_ht_create(&sman->owner_hash_tab, owner_order);
     76 	if (ret)
     77 		goto out1;
     78 	ret = drm_ht_create(&sman->user_hash_tab, user_order);
     79 	if (!ret)
     80 		goto out;
     81 
     82 	drm_ht_remove(&sman->owner_hash_tab);
     83 out1:
     84 	drm_free(sman->mm, num_managers * sizeof(*sman->mm), DRM_MEM_MM);
     85 out:
     86 	return ret;
     87 }
     88 
     89 static void *drm_sman_mm_allocate(void *private, unsigned long size,
     90 				  unsigned alignment)
     91 {
     92 	struct drm_mm *mm = (struct drm_mm *) private;
     93 	struct drm_mm_node *tmp;
     94 
     95 	tmp = drm_mm_search_free(mm, size, alignment, 1);
     96 	if (!tmp) {
     97 		return NULL;
     98 	}
     99 	/* This could be non-atomic, but we are called from a locked path */
    100 	tmp = drm_mm_get_block_atomic(tmp, size, alignment);
    101 	return tmp;
    102 }
    103 
    104 static void drm_sman_mm_free(void *private, void *ref)
    105 {
    106 	struct drm_mm_node *node = (struct drm_mm_node *) ref;
    107 
    108 	drm_mm_put_block(node);
    109 }
    110 
    111 static void drm_sman_mm_destroy(void *private)
    112 {
    113 	struct drm_mm *mm = (struct drm_mm *) private;
    114 	drm_mm_takedown(mm);
    115 	drm_free(mm, sizeof(*mm), DRM_MEM_MM);
    116 }
    117 
    118 static unsigned long drm_sman_mm_offset(void *private, void *ref)
    119 {
    120 	struct drm_mm_node *node = (struct drm_mm_node *) ref;
    121 	return node->start;
    122 }
    123 
    124 int
    125 drm_sman_set_range(struct drm_sman * sman, unsigned int manager,
    126 		   unsigned long start, unsigned long size)
    127 {
    128 	struct drm_sman_mm *sman_mm;
    129 	struct drm_mm *mm;
    130 	int ret;
    131 
    132 	KASSERT(manager < sman->num_managers);
    133 
    134 	sman_mm = &sman->mm[manager];
    135 	mm = malloc(sizeof(*mm), DRM_MEM_MM, M_NOWAIT | M_ZERO);
    136 	if (!mm) {
    137 		return -ENOMEM;
    138 	}
    139 	sman_mm->private = mm;
    140 	ret = drm_mm_init(mm, start, size);
    141 
    142 	if (ret) {
    143 		drm_free(mm, sizeof(*mm), DRM_MEM_MM);
    144 		return ret;
    145 	}
    146 
    147 	sman_mm->allocate = drm_sman_mm_allocate;
    148 	sman_mm->freem = drm_sman_mm_free;
    149 	sman_mm->destroy = drm_sman_mm_destroy;
    150 	sman_mm->offset = drm_sman_mm_offset;
    151 
    152 	return 0;
    153 }
    154 
    155 int
    156 drm_sman_set_manager(struct drm_sman * sman, unsigned int manager,
    157 		     struct drm_sman_mm * allocator)
    158 {
    159 	KASSERT(manager < sman->num_managers);
    160 	sman->mm[manager] = *allocator;
    161 
    162 	return 0;
    163 }
    164 
    165 static struct drm_owner_item *drm_sman_get_owner_item(struct drm_sman * sman,
    166 						 unsigned long owner)
    167 {
    168 	int ret;
    169 	struct drm_hash_item *owner_hash_item;
    170 	struct drm_owner_item *owner_item;
    171 
    172 	ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item);
    173 	if (!ret) {
    174 		return drm_hash_entry(owner_hash_item, struct drm_owner_item,
    175 				      owner_hash);
    176 	}
    177 
    178 	owner_item = malloc(sizeof(*owner_item), DRM_MEM_MM, M_NOWAIT | M_ZERO);
    179 	if (!owner_item)
    180 		goto out;
    181 
    182 	INIT_LIST_HEAD(&owner_item->mem_blocks);
    183 	owner_item->owner_hash.key = owner;
    184 	DRM_DEBUG("owner_item = %p, mem_blocks = %p\n", owner_item, &owner_item->mem_blocks);
    185 	if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash))
    186 		goto out1;
    187 
    188 	list_add_tail(&owner_item->sman_list, &sman->owner_items);
    189 	return owner_item;
    190 
    191 out1:
    192 	drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM);
    193 out:
    194 	return NULL;
    195 }
    196 
    197 struct drm_memblock_item *drm_sman_alloc(struct drm_sman *sman, unsigned int manager,
    198 				    unsigned long size, unsigned alignment,
    199 				    unsigned long owner)
    200 {
    201 	void *tmp;
    202 	struct drm_sman_mm *sman_mm;
    203 	struct drm_owner_item *owner_item;
    204 	struct drm_memblock_item *memblock;
    205 
    206 	KASSERT(manager < sman->num_managers);
    207 
    208 	sman_mm = &sman->mm[manager];
    209 	tmp = sman_mm->allocate(sman_mm->private, size, alignment);
    210 	if (!tmp) {
    211 		return NULL;
    212 	}
    213 
    214 	memblock = malloc(sizeof(*memblock), DRM_MEM_MM, M_NOWAIT | M_ZERO);
    215 	DRM_DEBUG("allocated mem_block %p\n", memblock);
    216 	if (!memblock)
    217 		goto out;
    218 
    219 	memblock->mm_info = tmp;
    220 	memblock->mm = sman_mm;
    221 	memblock->sman = sman;
    222 	INIT_LIST_HEAD(&memblock->owner_list);
    223 
    224 	if (drm_ht_just_insert_please
    225 	    (&sman->user_hash_tab, &memblock->user_hash,
    226 	     (unsigned long)memblock, 32, 0, 0))
    227 		goto out1;
    228 
    229 	owner_item = drm_sman_get_owner_item(sman, owner);
    230 	if (!owner_item)
    231 		goto out2;
    232 
    233 	DRM_DEBUG("owner_item = %p, mem_blocks = %p\n", owner_item, &owner_item->mem_blocks);
    234 	DRM_DEBUG("owner_list.prev = %p, mem_blocks.prev = %p\n", memblock->owner_list.prev, owner_item->mem_blocks.prev);
    235 	DRM_DEBUG("owner_list.next = %p, mem_blocks.next = %p\n", memblock->owner_list.next, owner_item->mem_blocks.next);
    236 	list_add_tail(&memblock->owner_list, &owner_item->mem_blocks);
    237 
    238 	DRM_DEBUG("Complete\n");
    239 	return memblock;
    240 
    241 out2:
    242 	drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash);
    243 out1:
    244 	drm_free(memblock, sizeof(*memblock), DRM_MEM_MM);
    245 out:
    246 	sman_mm->freem(sman_mm->private, tmp);
    247 
    248 	return NULL;
    249 }
    250 
    251 static void drm_sman_free(struct drm_memblock_item *item)
    252 {
    253 	struct drm_sman *sman = item->sman;
    254 
    255 	list_del(&item->owner_list);
    256 	drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash);
    257 	item->mm->freem(item->mm->private, item->mm_info);
    258 	drm_free(item, sizeof(*item), DRM_MEM_MM);
    259 }
    260 
    261 int drm_sman_free_key(struct drm_sman *sman, unsigned int key)
    262 {
    263 	struct drm_hash_item *hash_item;
    264 	struct drm_memblock_item *memblock_item;
    265 
    266 	if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item))
    267 		return -EINVAL;
    268 
    269 	memblock_item = drm_hash_entry(hash_item, struct drm_memblock_item,
    270 				       user_hash);
    271 	drm_sman_free(memblock_item);
    272 	return 0;
    273 }
    274 
    275 static void drm_sman_remove_owner(struct drm_sman *sman,
    276 				  struct drm_owner_item *owner_item)
    277 {
    278 	list_del(&owner_item->sman_list);
    279 	drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash);
    280 	drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM);
    281 }
    282 
    283 int drm_sman_owner_clean(struct drm_sman *sman, unsigned long owner)
    284 {
    285 
    286 	struct drm_hash_item *hash_item;
    287 	struct drm_owner_item *owner_item;
    288 
    289 	if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
    290 		return -1;
    291 	}
    292 
    293 	owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
    294 	DRM_DEBUG("cleaning owner_item %p\n", owner_item);
    295 	if (owner_item->mem_blocks.next == &owner_item->mem_blocks) {
    296 		drm_sman_remove_owner(sman, owner_item);
    297 		return -1;
    298 	}
    299 
    300 	return 0;
    301 }
    302 
    303 static void drm_sman_do_owner_cleanup(struct drm_sman *sman,
    304 				      struct drm_owner_item *owner_item)
    305 {
    306 	struct drm_memblock_item *entry, *next;
    307 
    308 	list_for_each_entry_safe(entry, next, &owner_item->mem_blocks,
    309 				 owner_list) {
    310 		DRM_DEBUG("freeing mem_block %p\n", entry);
    311 		drm_sman_free(entry);
    312 	}
    313 	drm_sman_remove_owner(sman, owner_item);
    314 }
    315 
    316 void drm_sman_owner_cleanup(struct drm_sman *sman, unsigned long owner)
    317 {
    318 
    319 	struct drm_hash_item *hash_item;
    320 	struct drm_owner_item *owner_item;
    321 
    322 	if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
    323 
    324 		return;
    325 	}
    326 
    327 	owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
    328 	drm_sman_do_owner_cleanup(sman, owner_item);
    329 }
    330 
    331 void drm_sman_cleanup(struct drm_sman *sman)
    332 {
    333 	struct drm_owner_item *entry, *next;
    334 	unsigned int i;
    335 	struct drm_sman_mm *sman_mm;
    336 
    337 	DRM_DEBUG("sman = %p, owner_items = %p\n",
    338 	    sman, &sman->owner_items);
    339 	list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) {
    340 		DRM_DEBUG("cleaning owner_item = %p\n", entry);
    341 		drm_sman_do_owner_cleanup(sman, entry);
    342 	}
    343 	if (sman->mm) {
    344 		for (i = 0; i < sman->num_managers; ++i) {
    345 			sman_mm = &sman->mm[i];
    346 			if (sman_mm->private) {
    347 				sman_mm->destroy(sman_mm->private);
    348 				sman_mm->private = NULL;
    349 			}
    350 		}
    351 	}
    352 }
    353