Path: blob/master/drivers/gpu/drm/nouveau/nouveau_mm.c
15112 views
/*1* Copyright 2010 Red Hat Inc.2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice shall be included in11* all copies or substantial portions of the Software.12*13* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL16* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR17* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,18* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR19* OTHER DEALINGS IN THE SOFTWARE.20*21* Authors: Ben Skeggs22*/2324#include "drmP.h"25#include "nouveau_drv.h"26#include "nouveau_mm.h"2728static inline void29region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a)30{31list_del(&a->nl_entry);32list_del(&a->fl_entry);33kfree(a);34}3536static struct nouveau_mm_node *37region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size)38{39struct nouveau_mm_node *b;4041if (a->length == size)42return a;4344b = kmalloc(sizeof(*b), GFP_KERNEL);45if (unlikely(b == NULL))46return NULL;4748b->offset = a->offset;49b->length = size;50b->type = a->type;51a->offset += size;52a->length -= size;53list_add_tail(&b->nl_entry, &a->nl_entry);54if (b->type == 0)55list_add_tail(&b->fl_entry, &a->fl_entry);56return b;57}5859#define node(root, dir) ((root)->nl_entry.dir == &rmm->nodes) ? NULL : \60list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)6162void63nouveau_mm_put(struct nouveau_mm *rmm, struct nouveau_mm_node *this)64{65struct nouveau_mm_node *prev = node(this, prev);66struct nouveau_mm_node *next = node(this, next);6768list_add(&this->fl_entry, &rmm->free);69this->type = 0;7071if (prev && prev->type == 0) {72prev->length += this->length;73region_put(rmm, this);74this = prev;75}7677if (next && next->type == 0) {78next->offset = this->offset;79next->length += this->length;80region_put(rmm, this);81}82}8384int85nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,86u32 align, struct nouveau_mm_node **pnode)87{88struct nouveau_mm_node *prev, *this, *next;89u32 min = size_nc ? size_nc : size;90u32 align_mask = align - 1;91u32 splitoff;92u32 s, e;9394list_for_each_entry(this, &rmm->free, fl_entry) {95e = this->offset + this->length;96s = this->offset;9798prev = node(this, prev);99if (prev && prev->type != type)100s = roundup(s, rmm->block_size);101102next = node(this, next);103if (next && next->type != type)104e = rounddown(e, rmm->block_size);105106s = (s + align_mask) & ~align_mask;107e &= ~align_mask;108if (s > e || e - s < min)109continue;110111splitoff = s - this->offset;112if (splitoff && !region_split(rmm, this, splitoff))113return -ENOMEM;114115this = region_split(rmm, this, min(size, e - s));116if (!this)117return -ENOMEM;118119this->type = type;120list_del(&this->fl_entry);121*pnode = this;122return 0;123}124125return -ENOSPC;126}127128int129nouveau_mm_init(struct nouveau_mm **prmm, u32 offset, u32 length, u32 block)130{131struct nouveau_mm *rmm;132struct nouveau_mm_node *heap;133134heap = kzalloc(sizeof(*heap), GFP_KERNEL);135if (!heap)136return -ENOMEM;137heap->offset = roundup(offset, block);138heap->length = rounddown(offset + length, block) - heap->offset;139140rmm = kzalloc(sizeof(*rmm), GFP_KERNEL);141if (!rmm) {142kfree(heap);143return -ENOMEM;144}145rmm->block_size = block;146mutex_init(&rmm->mutex);147INIT_LIST_HEAD(&rmm->nodes);148INIT_LIST_HEAD(&rmm->free);149list_add(&heap->nl_entry, &rmm->nodes);150list_add(&heap->fl_entry, &rmm->free);151152*prmm = rmm;153return 0;154}155156int157nouveau_mm_fini(struct nouveau_mm **prmm)158{159struct nouveau_mm *rmm = *prmm;160struct nouveau_mm_node *heap =161list_first_entry(&rmm->nodes, struct nouveau_mm_node, nl_entry);162163if (!list_is_singular(&rmm->nodes))164return -EBUSY;165166kfree(heap);167kfree(rmm);168*prmm = NULL;169return 0;170}171172173