1203287Srnoland/************************************************************************** 2203287Srnoland * 3203287Srnoland * Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., USA. 4203287Srnoland * All Rights Reserved. 5203287Srnoland * 6203287Srnoland * Permission is hereby granted, free of charge, to any person obtaining a 7203287Srnoland * copy of this software and associated documentation files (the 8203287Srnoland * "Software"), to deal in the Software without restriction, including 9203287Srnoland * without limitation the rights to use, copy, modify, merge, publish, 10203287Srnoland * distribute, sub license, and/or sell copies of the Software, and to 11203287Srnoland * permit persons to whom the Software is furnished to do so, subject to 12203287Srnoland * the following conditions: 13203287Srnoland * 14203287Srnoland * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15203287Srnoland * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16203287Srnoland * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 17203287Srnoland * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 18203287Srnoland * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19203287Srnoland * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20203287Srnoland * USE OR OTHER DEALINGS IN THE SOFTWARE. 21203287Srnoland * 22203287Srnoland * The above copyright notice and this permission notice (including the 23203287Srnoland * next paragraph) shall be included in all copies or substantial portions 24203287Srnoland * of the Software. 25203287Srnoland * 26203287Srnoland * 27203287Srnoland **************************************************************************/ 28203287Srnoland 29203287Srnoland#include <sys/cdefs.h> 30203287Srnoland__FBSDID("$FreeBSD: releng/10.3/sys/dev/drm/drm_sman.c 207118 2010-04-23 14:48:30Z rnoland $"); 31203287Srnoland 32203287Srnoland/* 33203287Srnoland * Simple memory manager interface that keeps track on allocate regions on a 34203287Srnoland * per "owner" basis. All regions associated with an "owner" can be released 35203287Srnoland * with a simple call. Typically if the "owner" exists. The owner is any 36203287Srnoland * "unsigned long" identifier. Can typically be a pointer to a file private 37203287Srnoland * struct or a context identifier. 38203287Srnoland * 39203287Srnoland * Authors: 40203287Srnoland * Thomas Hellstr��m <thomas-at-tungstengraphics-dot-com> 41203287Srnoland */ 42203287Srnoland 43203287Srnoland#include "dev/drm/drmP.h" 44203287Srnoland#include "dev/drm/drm_sman.h" 45203287Srnoland 46203287Srnolandstruct drm_owner_item { 47203287Srnoland struct drm_hash_item owner_hash; 48203287Srnoland struct list_head sman_list; 49203287Srnoland struct list_head mem_blocks; 50203287Srnoland}; 51203287Srnoland 52203287Srnolandvoid drm_sman_takedown(struct drm_sman * sman) 53203287Srnoland{ 54203287Srnoland drm_ht_remove(&sman->user_hash_tab); 55203287Srnoland drm_ht_remove(&sman->owner_hash_tab); 56203287Srnoland if (sman->mm) 57203287Srnoland drm_free(sman->mm, sman->num_managers * sizeof(*sman->mm), 58203287Srnoland DRM_MEM_MM); 59203287Srnoland} 60203287Srnoland 61203287Srnolandint 62203287Srnolanddrm_sman_init(struct drm_sman * sman, unsigned int num_managers, 63203287Srnoland unsigned int user_order, unsigned int owner_order) 64203287Srnoland{ 65203287Srnoland int ret = 0; 66203287Srnoland 67203287Srnoland sman->mm = (struct drm_sman_mm *) drm_calloc(num_managers, 68203287Srnoland sizeof(*sman->mm), DRM_MEM_MM); 69203287Srnoland if (!sman->mm) { 70203287Srnoland ret = -ENOMEM; 71203287Srnoland goto out; 72203287Srnoland } 73203287Srnoland sman->num_managers = num_managers; 74203287Srnoland INIT_LIST_HEAD(&sman->owner_items); 75203287Srnoland ret = drm_ht_create(&sman->owner_hash_tab, owner_order); 76203287Srnoland if (ret) 77203287Srnoland goto out1; 78203287Srnoland ret = drm_ht_create(&sman->user_hash_tab, user_order); 79203287Srnoland if (!ret) 80203287Srnoland goto out; 81203287Srnoland 82203287Srnoland drm_ht_remove(&sman->owner_hash_tab); 83203287Srnolandout1: 84203287Srnoland drm_free(sman->mm, num_managers * sizeof(*sman->mm), DRM_MEM_MM); 85203287Srnolandout: 86203287Srnoland return ret; 87203287Srnoland} 88203287Srnoland 89203287Srnolandstatic void *drm_sman_mm_allocate(void *private, unsigned long size, 90203287Srnoland unsigned alignment) 91203287Srnoland{ 92203287Srnoland struct drm_mm *mm = (struct drm_mm *) private; 93203287Srnoland struct drm_mm_node *tmp; 94203287Srnoland 95203287Srnoland tmp = drm_mm_search_free(mm, size, alignment, 1); 96203287Srnoland if (!tmp) { 97203287Srnoland return NULL; 98203287Srnoland } 99207118Srnoland /* This could be non-atomic, but we are called from a locked path */ 100207118Srnoland tmp = drm_mm_get_block_atomic(tmp, size, alignment); 101203287Srnoland return tmp; 102203287Srnoland} 103203287Srnoland 104203287Srnolandstatic void drm_sman_mm_free(void *private, void *ref) 105203287Srnoland{ 106203287Srnoland struct drm_mm_node *node = (struct drm_mm_node *) ref; 107203287Srnoland 108203287Srnoland drm_mm_put_block(node); 109203287Srnoland} 110203287Srnoland 111203287Srnolandstatic void drm_sman_mm_destroy(void *private) 112203287Srnoland{ 113203287Srnoland struct drm_mm *mm = (struct drm_mm *) private; 114203287Srnoland drm_mm_takedown(mm); 115203287Srnoland drm_free(mm, sizeof(*mm), DRM_MEM_MM); 116203287Srnoland} 117203287Srnoland 118203287Srnolandstatic unsigned long drm_sman_mm_offset(void *private, void *ref) 119203287Srnoland{ 120203287Srnoland struct drm_mm_node *node = (struct drm_mm_node *) ref; 121203287Srnoland return node->start; 122203287Srnoland} 123203287Srnoland 124203287Srnolandint 125203287Srnolanddrm_sman_set_range(struct drm_sman * sman, unsigned int manager, 126203287Srnoland unsigned long start, unsigned long size) 127203287Srnoland{ 128203287Srnoland struct drm_sman_mm *sman_mm; 129203287Srnoland struct drm_mm *mm; 130203287Srnoland int ret; 131203287Srnoland 132203287Srnoland KASSERT(manager < sman->num_managers, ("Invalid manager")); 133203287Srnoland 134203287Srnoland sman_mm = &sman->mm[manager]; 135207118Srnoland mm = malloc(sizeof(*mm), DRM_MEM_MM, M_NOWAIT | M_ZERO); 136203287Srnoland if (!mm) { 137203287Srnoland return -ENOMEM; 138203287Srnoland } 139203287Srnoland sman_mm->private = mm; 140203287Srnoland ret = drm_mm_init(mm, start, size); 141203287Srnoland 142203287Srnoland if (ret) { 143203287Srnoland drm_free(mm, sizeof(*mm), DRM_MEM_MM); 144203287Srnoland return ret; 145203287Srnoland } 146203287Srnoland 147203287Srnoland sman_mm->allocate = drm_sman_mm_allocate; 148203287Srnoland sman_mm->free = drm_sman_mm_free; 149203287Srnoland sman_mm->destroy = drm_sman_mm_destroy; 150203287Srnoland sman_mm->offset = drm_sman_mm_offset; 151203287Srnoland 152203287Srnoland return 0; 153203287Srnoland} 154203287Srnoland 155203287Srnolandint 156203287Srnolanddrm_sman_set_manager(struct drm_sman * sman, unsigned int manager, 157203287Srnoland struct drm_sman_mm * allocator) 158203287Srnoland{ 159203287Srnoland KASSERT(manager < sman->num_managers, ("Invalid manager")); 160203287Srnoland sman->mm[manager] = *allocator; 161203287Srnoland 162203287Srnoland return 0; 163203287Srnoland} 164203287Srnoland 165203287Srnolandstatic struct drm_owner_item *drm_sman_get_owner_item(struct drm_sman * sman, 166203287Srnoland unsigned long owner) 167203287Srnoland{ 168203287Srnoland int ret; 169203287Srnoland struct drm_hash_item *owner_hash_item; 170203287Srnoland struct drm_owner_item *owner_item; 171203287Srnoland 172203287Srnoland ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item); 173203287Srnoland if (!ret) { 174203287Srnoland return drm_hash_entry(owner_hash_item, struct drm_owner_item, 175203287Srnoland owner_hash); 176203287Srnoland } 177203287Srnoland 178207118Srnoland owner_item = malloc(sizeof(*owner_item), DRM_MEM_MM, M_NOWAIT | M_ZERO); 179203287Srnoland if (!owner_item) 180203287Srnoland goto out; 181203287Srnoland 182203287Srnoland INIT_LIST_HEAD(&owner_item->mem_blocks); 183203287Srnoland owner_item->owner_hash.key = owner; 184203287Srnoland DRM_DEBUG("owner_item = %p, mem_blocks = %p\n", owner_item, &owner_item->mem_blocks); 185203287Srnoland if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash)) 186203287Srnoland goto out1; 187203287Srnoland 188203287Srnoland list_add_tail(&owner_item->sman_list, &sman->owner_items); 189203287Srnoland return owner_item; 190203287Srnoland 191203287Srnolandout1: 192203287Srnoland drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM); 193203287Srnolandout: 194203287Srnoland return NULL; 195203287Srnoland} 196203287Srnoland 197203287Srnolandstruct drm_memblock_item *drm_sman_alloc(struct drm_sman *sman, unsigned int manager, 198203287Srnoland unsigned long size, unsigned alignment, 199203287Srnoland unsigned long owner) 200203287Srnoland{ 201203287Srnoland void *tmp; 202203287Srnoland struct drm_sman_mm *sman_mm; 203203287Srnoland struct drm_owner_item *owner_item; 204203287Srnoland struct drm_memblock_item *memblock; 205203287Srnoland 206203287Srnoland KASSERT(manager < sman->num_managers, ("Invalid manager")); 207203287Srnoland 208203287Srnoland sman_mm = &sman->mm[manager]; 209203287Srnoland tmp = sman_mm->allocate(sman_mm->private, size, alignment); 210203287Srnoland if (!tmp) { 211203287Srnoland return NULL; 212203287Srnoland } 213203287Srnoland 214207118Srnoland memblock = malloc(sizeof(*memblock), DRM_MEM_MM, M_NOWAIT | M_ZERO); 215203287Srnoland DRM_DEBUG("allocated mem_block %p\n", memblock); 216203287Srnoland if (!memblock) 217203287Srnoland goto out; 218203287Srnoland 219203287Srnoland memblock->mm_info = tmp; 220203287Srnoland memblock->mm = sman_mm; 221203287Srnoland memblock->sman = sman; 222203287Srnoland INIT_LIST_HEAD(&memblock->owner_list); 223203287Srnoland 224203287Srnoland if (drm_ht_just_insert_please 225203287Srnoland (&sman->user_hash_tab, &memblock->user_hash, 226203287Srnoland (unsigned long)memblock, 32, 0, 0)) 227203287Srnoland goto out1; 228203287Srnoland 229203287Srnoland owner_item = drm_sman_get_owner_item(sman, owner); 230203287Srnoland if (!owner_item) 231203287Srnoland goto out2; 232203287Srnoland 233203287Srnoland DRM_DEBUG("owner_item = %p, mem_blocks = %p\n", owner_item, &owner_item->mem_blocks); 234203287Srnoland DRM_DEBUG("owner_list.prev = %p, mem_blocks.prev = %p\n", memblock->owner_list.prev, owner_item->mem_blocks.prev); 235203287Srnoland DRM_DEBUG("owner_list.next = %p, mem_blocks.next = %p\n", memblock->owner_list.next, owner_item->mem_blocks.next); 236203287Srnoland list_add_tail(&memblock->owner_list, &owner_item->mem_blocks); 237203287Srnoland 238203287Srnoland DRM_DEBUG("Complete\n"); 239203287Srnoland return memblock; 240203287Srnoland 241203287Srnolandout2: 242203287Srnoland drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash); 243203287Srnolandout1: 244203287Srnoland drm_free(memblock, sizeof(*memblock), DRM_MEM_MM); 245203287Srnolandout: 246203287Srnoland sman_mm->free(sman_mm->private, tmp); 247203287Srnoland 248203287Srnoland return NULL; 249203287Srnoland} 250203287Srnoland 251203287Srnolandstatic void drm_sman_free(struct drm_memblock_item *item) 252203287Srnoland{ 253203287Srnoland struct drm_sman *sman = item->sman; 254203287Srnoland 255203287Srnoland list_del(&item->owner_list); 256203287Srnoland drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash); 257203287Srnoland item->mm->free(item->mm->private, item->mm_info); 258203287Srnoland drm_free(item, sizeof(*item), DRM_MEM_MM); 259203287Srnoland} 260203287Srnoland 261203287Srnolandint drm_sman_free_key(struct drm_sman *sman, unsigned int key) 262203287Srnoland{ 263203287Srnoland struct drm_hash_item *hash_item; 264203287Srnoland struct drm_memblock_item *memblock_item; 265203287Srnoland 266203287Srnoland if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item)) 267203287Srnoland return -EINVAL; 268203287Srnoland 269203287Srnoland memblock_item = drm_hash_entry(hash_item, struct drm_memblock_item, 270203287Srnoland user_hash); 271203287Srnoland drm_sman_free(memblock_item); 272203287Srnoland return 0; 273203287Srnoland} 274203287Srnoland 275203287Srnolandstatic void drm_sman_remove_owner(struct drm_sman *sman, 276203287Srnoland struct drm_owner_item *owner_item) 277203287Srnoland{ 278203287Srnoland list_del(&owner_item->sman_list); 279203287Srnoland drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash); 280203287Srnoland drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM); 281203287Srnoland} 282203287Srnoland 283203287Srnolandint drm_sman_owner_clean(struct drm_sman *sman, unsigned long owner) 284203287Srnoland{ 285203287Srnoland 286203287Srnoland struct drm_hash_item *hash_item; 287203287Srnoland struct drm_owner_item *owner_item; 288203287Srnoland 289203287Srnoland if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) { 290203287Srnoland return -1; 291203287Srnoland } 292203287Srnoland 293203287Srnoland owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash); 294203287Srnoland DRM_DEBUG("cleaning owner_item %p\n", owner_item); 295203287Srnoland if (owner_item->mem_blocks.next == &owner_item->mem_blocks) { 296203287Srnoland drm_sman_remove_owner(sman, owner_item); 297203287Srnoland return -1; 298203287Srnoland } 299203287Srnoland 300203287Srnoland return 0; 301203287Srnoland} 302203287Srnoland 303203287Srnolandstatic void drm_sman_do_owner_cleanup(struct drm_sman *sman, 304203287Srnoland struct drm_owner_item *owner_item) 305203287Srnoland{ 306203287Srnoland struct drm_memblock_item *entry, *next; 307203287Srnoland 308203287Srnoland list_for_each_entry_safe(entry, next, &owner_item->mem_blocks, 309203287Srnoland owner_list) { 310203287Srnoland DRM_DEBUG("freeing mem_block %p\n", entry); 311203287Srnoland drm_sman_free(entry); 312203287Srnoland } 313203287Srnoland drm_sman_remove_owner(sman, owner_item); 314203287Srnoland} 315203287Srnoland 316203287Srnolandvoid drm_sman_owner_cleanup(struct drm_sman *sman, unsigned long owner) 317203287Srnoland{ 318203287Srnoland 319203287Srnoland struct drm_hash_item *hash_item; 320203287Srnoland struct drm_owner_item *owner_item; 321203287Srnoland 322203287Srnoland if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) { 323203287Srnoland 324203287Srnoland return; 325203287Srnoland } 326203287Srnoland 327203287Srnoland owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash); 328203287Srnoland drm_sman_do_owner_cleanup(sman, owner_item); 329203287Srnoland} 330203287Srnoland 331203287Srnolandvoid drm_sman_cleanup(struct drm_sman *sman) 332203287Srnoland{ 333203287Srnoland struct drm_owner_item *entry, *next; 334203287Srnoland unsigned int i; 335203287Srnoland struct drm_sman_mm *sman_mm; 336203287Srnoland 337203287Srnoland DRM_DEBUG("sman = %p, owner_items = %p\n", 338203287Srnoland sman, &sman->owner_items); 339203287Srnoland list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) { 340203287Srnoland DRM_DEBUG("cleaning owner_item = %p\n", entry); 341203287Srnoland drm_sman_do_owner_cleanup(sman, entry); 342203287Srnoland } 343203287Srnoland if (sman->mm) { 344203287Srnoland for (i = 0; i < sman->num_managers; ++i) { 345203287Srnoland sman_mm = &sman->mm[i]; 346203287Srnoland if (sman_mm->private) { 347203287Srnoland sman_mm->destroy(sman_mm->private); 348203287Srnoland sman_mm->private = NULL; 349203287Srnoland } 350203287Srnoland } 351203287Srnoland } 352203287Srnoland} 353