linux/drivers/gpu/drm/drm_sman.c
<<
>>
Prefs
   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 * Simple memory manager interface that keeps track on allocate regions on a
  30 * per "owner" basis. All regions associated with an "owner" can be released
  31 * with a simple call. Typically if the "owner" exists. The owner is any
  32 * "unsigned long" identifier. Can typically be a pointer to a file private
  33 * struct or a context identifier.
  34 *
  35 * Authors:
  36 * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
  37 */
  38
  39#include "drm_sman.h"
  40
  41struct drm_owner_item {
  42        struct drm_hash_item owner_hash;
  43        struct list_head sman_list;
  44        struct list_head mem_blocks;
  45};
  46
  47void drm_sman_takedown(struct drm_sman * sman)
  48{
  49        drm_ht_remove(&sman->user_hash_tab);
  50        drm_ht_remove(&sman->owner_hash_tab);
  51        kfree(sman->mm);
  52}
  53
  54EXPORT_SYMBOL(drm_sman_takedown);
  55
  56int
  57drm_sman_init(struct drm_sman * sman, unsigned int num_managers,
  58              unsigned int user_order, unsigned int owner_order)
  59{
  60        int ret = 0;
  61
  62        sman->mm = (struct drm_sman_mm *) kcalloc(num_managers,
  63                                                  sizeof(*sman->mm),
  64                                                  GFP_KERNEL);
  65        if (!sman->mm) {
  66                ret = -ENOMEM;
  67                goto out;
  68        }
  69        sman->num_managers = num_managers;
  70        INIT_LIST_HEAD(&sman->owner_items);
  71        ret = drm_ht_create(&sman->owner_hash_tab, owner_order);
  72        if (ret)
  73                goto out1;
  74        ret = drm_ht_create(&sman->user_hash_tab, user_order);
  75        if (!ret)
  76                goto out;
  77
  78        drm_ht_remove(&sman->owner_hash_tab);
  79out1:
  80        kfree(sman->mm);
  81out:
  82        return ret;
  83}
  84
  85EXPORT_SYMBOL(drm_sman_init);
  86
  87static void *drm_sman_mm_allocate(void *private, unsigned long size,
  88                                  unsigned alignment)
  89{
  90        struct drm_mm *mm = (struct drm_mm *) private;
  91        struct drm_mm_node *tmp;
  92
  93        tmp = drm_mm_search_free(mm, size, alignment, 1);
  94        if (!tmp) {
  95                return NULL;
  96        }
  97        tmp = drm_mm_get_block(tmp, size, alignment);
  98        return tmp;
  99}
 100
 101static void drm_sman_mm_free(void *private, void *ref)
 102{
 103        struct drm_mm_node *node = (struct drm_mm_node *) ref;
 104
 105        drm_mm_put_block(node);
 106}
 107
 108static void drm_sman_mm_destroy(void *private)
 109{
 110        struct drm_mm *mm = (struct drm_mm *) private;
 111        drm_mm_takedown(mm);
 112        kfree(mm);
 113}
 114
 115static unsigned long drm_sman_mm_offset(void *private, void *ref)
 116{
 117        struct drm_mm_node *node = (struct drm_mm_node *) ref;
 118        return node->start;
 119}
 120
 121int
 122drm_sman_set_range(struct drm_sman * sman, unsigned int manager,
 123                   unsigned long start, unsigned long size)
 124{
 125        struct drm_sman_mm *sman_mm;
 126        struct drm_mm *mm;
 127        int ret;
 128
 129        BUG_ON(manager >= sman->num_managers);
 130
 131        sman_mm = &sman->mm[manager];
 132        mm = kzalloc(sizeof(*mm), GFP_KERNEL);
 133        if (!mm) {
 134                return -ENOMEM;
 135        }
 136        sman_mm->private = mm;
 137        ret = drm_mm_init(mm, start, size);
 138
 139        if (ret) {
 140                kfree(mm);
 141                return ret;
 142        }
 143
 144        sman_mm->allocate = drm_sman_mm_allocate;
 145        sman_mm->free = drm_sman_mm_free;
 146        sman_mm->destroy = drm_sman_mm_destroy;
 147        sman_mm->offset = drm_sman_mm_offset;
 148
 149        return 0;
 150}
 151
 152EXPORT_SYMBOL(drm_sman_set_range);
 153
 154int
 155drm_sman_set_manager(struct drm_sman * sman, unsigned int manager,
 156                     struct drm_sman_mm * allocator)
 157{
 158        BUG_ON(manager >= sman->num_managers);
 159        sman->mm[manager] = *allocator;
 160
 161        return 0;
 162}
 163EXPORT_SYMBOL(drm_sman_set_manager);
 164
 165static 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 = kzalloc(sizeof(*owner_item), GFP_KERNEL);
 179        if (!owner_item)
 180                goto out;
 181
 182        INIT_LIST_HEAD(&owner_item->mem_blocks);
 183        owner_item->owner_hash.key = owner;
 184        if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash))
 185                goto out1;
 186
 187        list_add_tail(&owner_item->sman_list, &sman->owner_items);
 188        return owner_item;
 189
 190out1:
 191        kfree(owner_item);
 192out:
 193        return NULL;
 194}
 195
 196struct drm_memblock_item *drm_sman_alloc(struct drm_sman *sman, unsigned int manager,
 197                                    unsigned long size, unsigned alignment,
 198                                    unsigned long owner)
 199{
 200        void *tmp;
 201        struct drm_sman_mm *sman_mm;
 202        struct drm_owner_item *owner_item;
 203        struct drm_memblock_item *memblock;
 204
 205        BUG_ON(manager >= sman->num_managers);
 206
 207        sman_mm = &sman->mm[manager];
 208        tmp = sman_mm->allocate(sman_mm->private, size, alignment);
 209
 210        if (!tmp) {
 211                return NULL;
 212        }
 213
 214        memblock = kzalloc(sizeof(*memblock), GFP_KERNEL);
 215
 216        if (!memblock)
 217                goto out;
 218
 219        memblock->mm_info = tmp;
 220        memblock->mm = sman_mm;
 221        memblock->sman = sman;
 222
 223        if (drm_ht_just_insert_please
 224            (&sman->user_hash_tab, &memblock->user_hash,
 225             (unsigned long)memblock, 32, 0, 0))
 226                goto out1;
 227
 228        owner_item = drm_sman_get_owner_item(sman, owner);
 229        if (!owner_item)
 230                goto out2;
 231
 232        list_add_tail(&memblock->owner_list, &owner_item->mem_blocks);
 233
 234        return memblock;
 235
 236out2:
 237        drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash);
 238out1:
 239        kfree(memblock);
 240out:
 241        sman_mm->free(sman_mm->private, tmp);
 242
 243        return NULL;
 244}
 245
 246EXPORT_SYMBOL(drm_sman_alloc);
 247
 248static void drm_sman_free(struct drm_memblock_item *item)
 249{
 250        struct drm_sman *sman = item->sman;
 251
 252        list_del(&item->owner_list);
 253        drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash);
 254        item->mm->free(item->mm->private, item->mm_info);
 255        kfree(item);
 256}
 257
 258int drm_sman_free_key(struct drm_sman *sman, unsigned int key)
 259{
 260        struct drm_hash_item *hash_item;
 261        struct drm_memblock_item *memblock_item;
 262
 263        if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item))
 264                return -EINVAL;
 265
 266        memblock_item = drm_hash_entry(hash_item, struct drm_memblock_item,
 267                                       user_hash);
 268        drm_sman_free(memblock_item);
 269        return 0;
 270}
 271
 272EXPORT_SYMBOL(drm_sman_free_key);
 273
 274static void drm_sman_remove_owner(struct drm_sman *sman,
 275                                  struct drm_owner_item *owner_item)
 276{
 277        list_del(&owner_item->sman_list);
 278        drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash);
 279        kfree(owner_item);
 280}
 281
 282int drm_sman_owner_clean(struct drm_sman *sman, unsigned long owner)
 283{
 284
 285        struct drm_hash_item *hash_item;
 286        struct drm_owner_item *owner_item;
 287
 288        if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
 289                return -1;
 290        }
 291
 292        owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
 293        if (owner_item->mem_blocks.next == &owner_item->mem_blocks) {
 294                drm_sman_remove_owner(sman, owner_item);
 295                return -1;
 296        }
 297
 298        return 0;
 299}
 300
 301EXPORT_SYMBOL(drm_sman_owner_clean);
 302
 303static 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_sman_free(entry);
 311        }
 312        drm_sman_remove_owner(sman, owner_item);
 313}
 314
 315void drm_sman_owner_cleanup(struct drm_sman *sman, unsigned long owner)
 316{
 317
 318        struct drm_hash_item *hash_item;
 319        struct drm_owner_item *owner_item;
 320
 321        if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
 322
 323                return;
 324        }
 325
 326        owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
 327        drm_sman_do_owner_cleanup(sman, owner_item);
 328}
 329
 330EXPORT_SYMBOL(drm_sman_owner_cleanup);
 331
 332void drm_sman_cleanup(struct drm_sman *sman)
 333{
 334        struct drm_owner_item *entry, *next;
 335        unsigned int i;
 336        struct drm_sman_mm *sman_mm;
 337
 338        list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) {
 339                drm_sman_do_owner_cleanup(sman, entry);
 340        }
 341        if (sman->mm) {
 342                for (i = 0; i < sman->num_managers; ++i) {
 343                        sman_mm = &sman->mm[i];
 344                        if (sman_mm->private) {
 345                                sman_mm->destroy(sman_mm->private);
 346                                sman_mm->private = NULL;
 347                        }
 348                }
 349        }
 350}
 351
 352EXPORT_SYMBOL(drm_sman_cleanup);
 353