drm_mm.c revision 1450
1450N/A/*
1450N/A * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
1450N/A */
1450N/A
1450N/A/**************************************************************************
1450N/A *
1450N/A * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
1450N/A * Copyright (c) 2009, 2013, Intel Corporation.
1450N/A * All Rights Reserved.
1450N/A *
1450N/A * Permission is hereby granted, free of charge, to any person obtaining a
1450N/A * copy of this software and associated documentation files (the
1450N/A * "Software"), to deal in the Software without restriction, including
1450N/A * without limitation the rights to use, copy, modify, merge, publish,
1450N/A * distribute, sub license, and/or sell copies of the Software, and to
1450N/A * permit persons to whom the Software is furnished to do so, subject to
1450N/A * the following conditions:
1450N/A *
1450N/A * The above copyright notice and this permission notice (including the
1450N/A * next paragraph) shall be included in all copies or substantial portions
1450N/A * of the Software.
1450N/A *
1450N/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1450N/A * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1450N/A * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
1450N/A * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
1450N/A * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
1450N/A * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
1450N/A * USE OR OTHER DEALINGS IN THE SOFTWARE.
1450N/A *
1450N/A *
1450N/A **************************************************************************/
1450N/A
1450N/A/*
1450N/A * Generic simple memory manager implementation. Intended to be used as a base
1450N/A * class implementation for more advanced memory managers.
1450N/A *
1450N/A * Note that the algorithm used is quite simple and there might be substantial
1450N/A * performance gains if a smarter free list is implemented. Currently it is just an
1450N/A * unordered stack of free regions. This could easily be improved if an RB-tree
1450N/A * is used instead. At least if we expect heavy fragmentation.
1450N/A *
1450N/A * Aligned allocations can also see improvement.
1450N/A *
1450N/A * Authors:
1450N/A * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
1450N/A */
1450N/A
1450N/A#include "drmP.h"
1450N/A#include "drm_mm.h"
1450N/A#define MM_UNUSED_TARGET 4
1450N/A
1450N/Astatic struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic)
1450N/A{
1450N/A struct drm_mm_node *child;
1450N/A
1450N/A if (atomic)
1450N/A child = kzalloc(sizeof(*child), GFP_ATOMIC);
1450N/A else
1450N/A child = kzalloc(sizeof(*child), GFP_KERNEL);
1450N/A
1450N/A if (unlikely(child == NULL)) {
1450N/A spin_lock(&mm->unused_lock);
1450N/A if (list_empty(&mm->unused_nodes))
1450N/A child = NULL;
1450N/A else {
1450N/A child =
1450N/A list_entry(mm->unused_nodes.next,
1450N/A struct drm_mm_node, node_list);
1450N/A list_del(&child->node_list);
1450N/A --mm->num_unused;
1450N/A }
1450N/A spin_unlock(&mm->unused_lock);
1450N/A }
1450N/A return child;
1450N/A}
1450N/A
1450N/A/* drm_mm_pre_get() - pre allocate drm_mm_node structure
1450N/A * drm_mm: memory manager struct we are pre-allocating for
1450N/A *
1450N/A * Returns 0 on success or -ENOMEM if allocation fails.
1450N/A */
1450N/Aint drm_mm_pre_get(struct drm_mm *mm)
1450N/A{
1450N/A struct drm_mm_node *node;
1450N/A
1450N/A spin_lock(&mm->unused_lock);
1450N/A while (mm->num_unused < MM_UNUSED_TARGET) {
1450N/A spin_unlock(&mm->unused_lock);
1450N/A node = kzalloc(sizeof(*node), GFP_KERNEL);
1450N/A spin_lock(&mm->unused_lock);
1450N/A
1450N/A if (unlikely(node == NULL)) {
1450N/A int ret = (mm->num_unused < 2) ? -ENOMEM : 0;
1450N/A spin_unlock(&mm->unused_lock);
1450N/A return ret;
1450N/A }
1450N/A ++mm->num_unused;
1450N/A list_add_tail(&node->node_list, &mm->unused_nodes, (caddr_t)node);
1450N/A }
1450N/A spin_unlock(&mm->unused_lock);
1450N/A return 0;
1450N/A}
1450N/A
1450N/Astatic void drm_mm_insert_helper(struct drm_mm_node *hole_node,
1450N/A struct drm_mm_node *node,
1450N/A unsigned long size, unsigned alignment,
1450N/A unsigned long color)
1450N/A{
1450N/A struct drm_mm *mm = hole_node->mm;
1450N/A unsigned long hole_start = drm_mm_hole_node_start(hole_node);
1450N/A unsigned long hole_end = drm_mm_hole_node_end(hole_node);
1450N/A unsigned long adj_start = hole_start;
1450N/A unsigned long adj_end = hole_end;
1450N/A
1450N/A BUG_ON(node->allocated);
1450N/A
1450N/A if (mm->color_adjust)
1450N/A mm->color_adjust(hole_node, color, &adj_start, &adj_end);
1450N/A
1450N/A if (alignment) {
1450N/A unsigned tmp = adj_start % alignment;
1450N/A if (tmp)
1450N/A adj_start += alignment - tmp;
1450N/A }
1450N/A
1450N/A if (adj_start == hole_start) {
1450N/A hole_node->hole_follows = 0;
1450N/A list_del(&hole_node->hole_stack);
1450N/A }
1450N/A
1450N/A node->start = adj_start;
1450N/A node->size = size;
1450N/A node->mm = mm;
1450N/A node->color = color;
1450N/A node->allocated = 1;
1450N/A
1450N/A INIT_LIST_HEAD(&node->hole_stack);
1450N/A list_add(&node->node_list, &hole_node->node_list, (caddr_t)node);
1450N/A
1450N/A BUG_ON(node->start + node->size > adj_end);
1450N/A
1450N/A node->hole_follows = 0;
1450N/A if (__drm_mm_hole_node_start(node) < hole_end) {
1450N/A list_add(&node->hole_stack, &mm->hole_stack, (caddr_t)node);
1450N/A node->hole_follows = 1;
1450N/A }
1450N/A}
1450N/A
1450N/Astruct drm_mm_node *drm_mm_create_block(struct drm_mm *mm,
1450N/A unsigned long start,
1450N/A unsigned long size,
1450N/A bool atomic)
1450N/A{
1450N/A struct drm_mm_node *hole, *node;
1450N/A unsigned long end = start + size;
1450N/A unsigned long hole_start;
1450N/A unsigned long hole_end;
1450N/A
1450N/A drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
1450N/A if (hole_start > start || hole_end < end)
1450N/A continue;
1450N/A
1450N/A node = drm_mm_kmalloc(mm, atomic);
1450N/A if (unlikely(node == NULL))
1450N/A return NULL;
1450N/A
1450N/A node->start = start;
1450N/A node->size = size;
1450N/A node->mm = mm;
1450N/A node->allocated = 1;
1450N/A
1450N/A INIT_LIST_HEAD(&node->hole_stack);
1450N/A list_add(&node->node_list, &hole->node_list, (caddr_t)node);
1450N/A
1450N/A if (start == hole_start) {
1450N/A hole->hole_follows = 0;
1450N/A list_del_init(&hole->hole_stack);
1450N/A }
1450N/A
1450N/A node->hole_follows = 0;
1450N/A if (end != hole_end) {
1450N/A list_add(&node->hole_stack, &mm->hole_stack, (caddr_t)node);
1450N/A node->hole_follows = 1;
1450N/A }
1450N/A
1450N/A return node;
1450N/A }
1450N/A
1450N/A DRM_ERROR("no hole found for block 0x%lx + 0x%lx\n", start, size);
1450N/A return NULL;
1450N/A}
1450N/A
1450N/Astruct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node,
1450N/A unsigned long size,
1450N/A unsigned alignment,
1450N/A unsigned long color,
1450N/A int atomic)
1450N/A{
1450N/A struct drm_mm_node *node;
1450N/A
1450N/A node = drm_mm_kmalloc(hole_node->mm, atomic);
1450N/A if (unlikely(node == NULL))
1450N/A return NULL;
1450N/A
1450N/A drm_mm_insert_helper(hole_node, node, size, alignment, color);
1450N/A
1450N/A return node;
1450N/A}
1450N/A
1450N/A/**
1450N/A * Search for free space and insert a preallocated memory node. Returns
1450N/A * -ENOSPC if no suitable free area is available. The preallocated memory node
1450N/A * must be cleared.
1450N/A */
1450N/Aint drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
1450N/A unsigned long size, unsigned alignment,
1450N/A unsigned long color)
1450N/A{
1450N/A struct drm_mm_node *hole_node;
1450N/A
1450N/A hole_node = drm_mm_search_free_generic(mm, size, alignment,
1450N/A color, 0);
1450N/A if (!hole_node)
1450N/A return -ENOSPC;
1450N/A
1450N/A drm_mm_insert_helper(hole_node, node, size, alignment, color);
1450N/A return 0;
1450N/A}
1450N/A
1450N/Aint drm_mm_insert_node(struct drm_mm *mm, struct drm_mm_node *node,
1450N/A unsigned long size, unsigned alignment)
1450N/A{
1450N/A return drm_mm_insert_node_generic(mm, node, size, alignment, 0);
1450N/A}
1450N/A
1450N/Astatic void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
1450N/A struct drm_mm_node *node,
1450N/A unsigned long size, unsigned alignment,
1450N/A unsigned long color,
1450N/A unsigned long start, unsigned long end)
1450N/A{
1450N/A struct drm_mm *mm = hole_node->mm;
1450N/A unsigned long hole_start = drm_mm_hole_node_start(hole_node);
1450N/A unsigned long hole_end = drm_mm_hole_node_end(hole_node);
1450N/A unsigned long adj_start = hole_start;
1450N/A unsigned long adj_end = hole_end;
1450N/A
1450N/A BUG_ON(!hole_node->hole_follows || node->allocated);
1450N/A
1450N/A if (adj_start < start)
1450N/A adj_start = start;
1450N/A if (adj_end > end)
1450N/A adj_end = end;
1450N/A
1450N/A if (mm->color_adjust)
1450N/A mm->color_adjust(hole_node, color, &adj_start, &adj_end);
1450N/A
1450N/A if (alignment) {
1450N/A unsigned tmp = adj_start % alignment;
1450N/A if (tmp)
1450N/A adj_start += alignment - tmp;
1450N/A }
1450N/A
1450N/A if (adj_start == hole_start) {
1450N/A hole_node->hole_follows = 0;
1450N/A list_del(&hole_node->hole_stack);
1450N/A }
1450N/A
1450N/A node->start = adj_start;
1450N/A node->size = size;
1450N/A node->mm = mm;
1450N/A node->color = color;
1450N/A node->allocated = 1;
1450N/A
1450N/A INIT_LIST_HEAD(&node->hole_stack);
1450N/A list_add(&node->node_list, &hole_node->node_list, (caddr_t)node);
1450N/A
1450N/A BUG_ON(node->start + node->size > adj_end);
1450N/A BUG_ON(node->start + node->size > end);
1450N/A
1450N/A node->hole_follows = 0;
1450N/A if (__drm_mm_hole_node_start(node) < hole_end) {
1450N/A list_add(&node->hole_stack, &mm->hole_stack, (caddr_t)node);
1450N/A node->hole_follows = 1;
1450N/A }
1450N/A}
1450N/A
1450N/Astruct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *hole_node,
1450N/A unsigned long size,
1450N/A unsigned alignment,
1450N/A unsigned long color,
1450N/A unsigned long start,
1450N/A unsigned long end,
1450N/A int atomic)
1450N/A{
1450N/A struct drm_mm_node *node;
1450N/A
1450N/A node = drm_mm_kmalloc(hole_node->mm, atomic);
1450N/A if (unlikely(node == NULL))
1450N/A return NULL;
1450N/A
1450N/A drm_mm_insert_helper_range(hole_node, node, size, alignment, color,
1450N/A start, end);
1450N/A
1450N/A return node;
1450N/A}
1450N/A
1450N/A/**
1450N/A * Search for free space and insert a preallocated memory node. Returns
1450N/A * -ENOSPC if no suitable free area is available. This is for range
1450N/A * restricted allocations. The preallocated memory node must be cleared.
1450N/A */
1450N/Aint drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
1450N/A unsigned long size, unsigned alignment, unsigned long color,
1450N/A unsigned long start, unsigned long end)
1450N/A{
1450N/A struct drm_mm_node *hole_node;
1450N/A
1450N/A hole_node = drm_mm_search_free_in_range_generic(mm,
1450N/A size, alignment, color,
1450N/A start, end, 0);
1450N/A if (!hole_node)
1450N/A return -ENOSPC;
1450N/A
1450N/A drm_mm_insert_helper_range(hole_node, node,
1450N/A size, alignment, color,
1450N/A start, end);
1450N/A return 0;
1450N/A}
1450N/A
1450N/Aint drm_mm_insert_node_in_range(struct drm_mm *mm, struct drm_mm_node *node,
1450N/A unsigned long size, unsigned alignment,
1450N/A unsigned long start, unsigned long end)
1450N/A{
1450N/A return drm_mm_insert_node_in_range_generic(mm, node, size, alignment, 0, start, end);
1450N/A}
1450N/A
1450N/A/**
1450N/A * Remove a memory node from the allocator.
1450N/A */
1450N/Avoid drm_mm_remove_node(struct drm_mm_node *node)
1450N/A{
1450N/A struct drm_mm *mm = node->mm;
1450N/A struct drm_mm_node *prev_node;
1450N/A
1450N/A BUG_ON(node->scanned_block || node->scanned_prev_free
1450N/A || node->scanned_next_free);
1450N/A
1450N/A prev_node =
1450N/A list_entry(node->node_list.prev, struct drm_mm_node, node_list);
1450N/A
1450N/A if (node->hole_follows) {
1450N/A BUG_ON(__drm_mm_hole_node_start(node) ==
1450N/A __drm_mm_hole_node_end(node));
1450N/A list_del(&node->hole_stack);
1450N/A /* LINTED */
1450N/A } else
1450N/A BUG_ON(__drm_mm_hole_node_start(node) !=
1450N/A __drm_mm_hole_node_end(node));
1450N/A
1450N/A
1450N/A if (!prev_node->hole_follows) {
1450N/A prev_node->hole_follows = 1;
1450N/A list_add(&prev_node->hole_stack, &mm->hole_stack, (caddr_t)prev_node);
1450N/A } else
1450N/A list_move(&prev_node->hole_stack, &mm->hole_stack, (caddr_t)prev_node);
1450N/A
1450N/A list_del(&node->node_list);
1450N/A node->allocated = 0;
1450N/A}
1450N/A
1450N/A/*
1450N/A * Remove a memory node from the allocator and free the allocated struct
1450N/A * drm_mm_node. Only to be used on a struct drm_mm_node obtained by one of the
1450N/A * drm_mm_get_block functions.
1450N/A */
1450N/Avoid drm_mm_put_block(struct drm_mm_node *node)
1450N/A{
1450N/A
1450N/A struct drm_mm *mm = node->mm;
1450N/A
1450N/A drm_mm_remove_node(node);
1450N/A
1450N/A spin_lock(&mm->unused_lock);
1450N/A if (mm->num_unused < MM_UNUSED_TARGET) {
1450N/A list_add(&node->node_list, &mm->unused_nodes, (caddr_t)node);
1450N/A ++mm->num_unused;
1450N/A } else
1450N/A kfree(node, sizeof(struct drm_mm_node));
1450N/A spin_unlock(&mm->unused_lock);
1450N/A}
1450N/A
1450N/Astatic int check_free_hole(unsigned long start, unsigned long end,
1450N/A unsigned long size, unsigned alignment)
1450N/A{
1450N/A if (end - start < size)
1450N/A return 0;
1450N/A
1450N/A if (alignment) {
1450N/A unsigned tmp = start % alignment;
1450N/A if (tmp)
1450N/A start += alignment - tmp;
1450N/A }
1450N/A
1450N/A return end >= start + size;
1450N/A}
1450N/A
1450N/Astruct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
1450N/A unsigned long size,
1450N/A unsigned alignment,
1450N/A unsigned long color,
1450N/A bool best_match)
1450N/A{
1450N/A struct drm_mm_node *entry;
1450N/A struct drm_mm_node *best;
1450N/A unsigned long adj_start;
1450N/A unsigned long adj_end;
1450N/A unsigned long best_size;
1450N/A
1450N/A BUG_ON(mm->scanned_blocks);
1450N/A
1450N/A best = NULL;
1450N/A best_size = ~0UL;
1450N/A
1450N/A drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
1450N/A if (mm->color_adjust) {
1450N/A mm->color_adjust(entry, color, &adj_start, &adj_end);
1450N/A if (adj_end <= adj_start)
1450N/A continue;
1450N/A }
1450N/A
1450N/A if (!check_free_hole(adj_start, adj_end, size, alignment))
1450N/A continue;
1450N/A
1450N/A if (!best_match)
1450N/A return entry;
1450N/A
1450N/A if (entry->size < best_size) {
1450N/A best = entry;
1450N/A best_size = entry->size;
1450N/A }
1450N/A }
1450N/A
1450N/A return best;
1450N/A}
1450N/A
1450N/Astruct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
1450N/A unsigned long size,
1450N/A unsigned alignment,
1450N/A unsigned long color,
1450N/A unsigned long start,
1450N/A unsigned long end,
1450N/A bool best_match)
1450N/A{
1450N/A struct drm_mm_node *entry;
1450N/A struct drm_mm_node *best;
1450N/A unsigned long adj_start;
1450N/A unsigned long adj_end;
1450N/A unsigned long best_size;
1450N/A
1450N/A BUG_ON(mm->scanned_blocks);
1450N/A
1450N/A best = NULL;
1450N/A best_size = ~0UL;
1450N/A
1450N/A drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
1450N/A if (adj_start < start)
1450N/A adj_start = start;
1450N/A if (adj_end > end)
1450N/A adj_end = end;
1450N/A
1450N/A if (mm->color_adjust) {
1450N/A mm->color_adjust(entry, color, &adj_start, &adj_end);
1450N/A if (adj_end <= adj_start)
1450N/A continue;
1450N/A }
1450N/A
1450N/A if (!check_free_hole(adj_start, adj_end, size, alignment))
1450N/A continue;
1450N/A
1450N/A if (!best_match)
1450N/A return entry;
1450N/A
1450N/A if (entry->size < best_size) {
1450N/A best = entry;
1450N/A best_size = entry->size;
1450N/A }
1450N/A }
1450N/A
1450N/A return best;
1450N/A}
1450N/A
1450N/A/**
1450N/A * Moves an allocation. To be used with embedded struct drm_mm_node.
1450N/A */
1450N/Avoid drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
1450N/A{
1450N/A list_replace(&old->node_list, &new->node_list);
1450N/A list_replace(&old->hole_stack, &new->hole_stack);
1450N/A new->hole_follows = old->hole_follows;
1450N/A new->mm = old->mm;
1450N/A new->start = old->start;
1450N/A new->size = old->size;
1450N/A new->color = old->color;
1450N/A
1450N/A old->allocated = 0;
1450N/A new->allocated = 1;
1450N/A}
1450N/A
1450N/A/**
1450N/A * Initializa lru scanning.
1450N/A *
1450N/A * This simply sets up the scanning routines with the parameters for the desired
1450N/A * hole.
1450N/A *
1450N/A * Warning: As long as the scan list is non-empty, no other operations than
1450N/A * adding/removing nodes to/from the scan list are allowed.
1450N/A */
1450N/Avoid drm_mm_init_scan(struct drm_mm *mm,
1450N/A unsigned long size,
1450N/A unsigned alignment,
1450N/A unsigned long color)
1450N/A{
1450N/A mm->scan_color = color;
1450N/A mm->scan_alignment = alignment;
1450N/A mm->scan_size = size;
1450N/A mm->scanned_blocks = 0;
1450N/A mm->scan_hit_start = 0;
1450N/A mm->scan_hit_end = 0;
1450N/A mm->scan_check_range = 0;
1450N/A mm->prev_scanned_node = NULL;
1450N/A}
1450N/A
1450N/A/**
1450N/A * Initializa lru scanning.
1450N/A *
1450N/A * This simply sets up the scanning routines with the parameters for the desired
1450N/A * hole. This version is for range-restricted scans.
1450N/A *
1450N/A * Warning: As long as the scan list is non-empty, no other operations than
1450N/A * adding/removing nodes to/from the scan list are allowed.
1450N/A */
1450N/Avoid drm_mm_init_scan_with_range(struct drm_mm *mm,
1450N/A unsigned long size,
1450N/A unsigned alignment,
1450N/A unsigned long color,
1450N/A unsigned long start,
1450N/A unsigned long end)
1450N/A{
1450N/A mm->scan_color = color;
1450N/A mm->scan_alignment = alignment;
1450N/A mm->scan_size = size;
1450N/A mm->scanned_blocks = 0;
1450N/A mm->scan_hit_start = 0;
1450N/A mm->scan_hit_end = 0;
1450N/A mm->scan_start = start;
1450N/A mm->scan_end = end;
1450N/A mm->scan_check_range = 1;
1450N/A mm->prev_scanned_node = NULL;
1450N/A}
1450N/A
1450N/A/**
1450N/A * Add a node to the scan list that might be freed to make space for the desired
1450N/A * hole.
1450N/A *
1450N/A * Returns non-zero, if a hole has been found, zero otherwise.
1450N/A */
1450N/Aint drm_mm_scan_add_block(struct drm_mm_node *node)
1450N/A{
1450N/A struct drm_mm *mm = node->mm;
1450N/A struct drm_mm_node *prev_node;
1450N/A unsigned long hole_start, hole_end;
1450N/A unsigned long adj_start, adj_end;
1450N/A
1450N/A mm->scanned_blocks++;
1450N/A
1450N/A BUG_ON(node->scanned_block);
1450N/A node->scanned_block = 1;
1450N/A
1450N/A prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
1450N/A node_list);
1450N/A
1450N/A node->scanned_preceeds_hole = prev_node->hole_follows;
1450N/A prev_node->hole_follows = 1;
1450N/A list_del(&node->node_list);
1450N/A node->node_list.prev = &prev_node->node_list;
1450N/A node->node_list.next = &mm->prev_scanned_node->node_list;
1450N/A mm->prev_scanned_node = node;
1450N/A
1450N/A adj_start = hole_start = drm_mm_hole_node_start(prev_node);
1450N/A adj_end = hole_end = drm_mm_hole_node_end(prev_node);
1450N/A
1450N/A if (mm->scan_check_range) {
1450N/A if (adj_start < mm->scan_start)
1450N/A adj_start = mm->scan_start;
1450N/A if (adj_end > mm->scan_end)
1450N/A adj_end = mm->scan_end;
1450N/A }
1450N/A
1450N/A if (mm->color_adjust)
1450N/A mm->color_adjust(prev_node, mm->scan_color,
1450N/A &adj_start, &adj_end);
1450N/A
1450N/A if (check_free_hole(adj_start , adj_end,
1450N/A mm->scan_size, mm->scan_alignment)) {
1450N/A mm->scan_hit_start = hole_start;
1450N/A mm->scan_hit_end = hole_end;
1450N/A return 1;
1450N/A }
1450N/A
1450N/A return 0;
1450N/A}
1450N/A
1450N/A/**
1450N/A * Remove a node from the scan list.
1450N/A *
1450N/A * Nodes _must_ be removed in the exact same order from the scan list as they
1450N/A * have been added, otherwise the internal state of the memory manager will be
1450N/A * corrupted.
1450N/A *
1450N/A * When the scan list is empty, the selected memory nodes can be freed. An
1450N/A * immediatly following drm_mm_search_free with best_match = 0 will then return
1450N/A * the just freed block (because its at the top of the free_stack list).
1450N/A *
1450N/A * Returns one if this block should be evicted, zero otherwise. Will always
1450N/A * return zero when no hole has been found.
1450N/A */
1450N/Aint drm_mm_scan_remove_block(struct drm_mm_node *node)
1450N/A{
1450N/A struct drm_mm *mm = node->mm;
1450N/A struct drm_mm_node *prev_node;
1450N/A
1450N/A mm->scanned_blocks--;
1450N/A
1450N/A BUG_ON(!node->scanned_block);
1450N/A node->scanned_block = 0;
1450N/A
1450N/A prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
1450N/A node_list);
1450N/A
1450N/A prev_node->hole_follows = node->scanned_preceeds_hole;
1450N/A list_add(&node->node_list, &prev_node->node_list, (caddr_t)node);
1450N/A
1450N/A return (drm_mm_hole_node_end(node) > mm->scan_hit_start &&
1450N/A node->start < mm->scan_hit_end);
1450N/A}
1450N/A
1450N/A
1450N/Aint drm_mm_clean(struct drm_mm * mm)
1450N/A{
1450N/A struct list_head *head = &mm->head_node.node_list;
1450N/A
1450N/A return (head->next->next == head);
1450N/A}
1450N/A
1450N/A
1450N/Avoid drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
1450N/A{
1450N/A INIT_LIST_HEAD(&mm->hole_stack);
1450N/A INIT_LIST_HEAD(&mm->unused_nodes);
1450N/A mm->num_unused = 0;
1450N/A mm->scanned_blocks = 0;
1450N/A spin_lock_init(&mm->unused_lock);
1450N/A
1450N/A /* Clever trick to avoid a special case in the free hole tracking. */
1450N/A INIT_LIST_HEAD(&mm->head_node.node_list);
1450N/A INIT_LIST_HEAD(&mm->head_node.hole_stack);
1450N/A mm->head_node.hole_follows = 1;
1450N/A mm->head_node.scanned_block = 0;
1450N/A mm->head_node.scanned_prev_free = 0;
1450N/A mm->head_node.scanned_next_free = 0;
1450N/A mm->head_node.mm = mm;
1450N/A mm->head_node.start = start + size;
1450N/A mm->head_node.size = start - mm->head_node.start;
1450N/A mm->head_node.node_list.contain_ptr = (caddr_t)&mm->head_node;
1450N/A list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack, (caddr_t)&mm->head_node);
1450N/A mm->color_adjust = NULL;
1450N/A}
1450N/A
1450N/A
1450N/Avoid drm_mm_takedown(struct drm_mm * mm)
1450N/A{
1450N/A struct drm_mm_node *entry, *next;
1450N/A
1450N/A if (!list_empty(&mm->head_node.node_list)) {
1450N/A DRM_ERROR("Memory manager not clean. Delaying takedown\n");
1450N/A return;
1450N/A }
1450N/A
1450N/A spin_lock(&mm->unused_lock);
1450N/A list_for_each_entry_safe(entry, next, struct drm_mm_node, &mm->unused_nodes, node_list) {
1450N/A list_del(&entry->node_list);
1450N/A kfree(entry, sizeof(struct drm_mm_node));
1450N/A --mm->num_unused;
1450N/A }
1450N/A spin_unlock(&mm->unused_lock);
1450N/A
1450N/A BUG_ON(mm->num_unused != 0);
1450N/A}
1450N/A
1450N/Astatic unsigned long drm_mm_debug_hole(struct drm_mm_node *entry,
1450N/A const char *prefix)
1450N/A{
1450N/A unsigned long hole_start, hole_end, hole_size;
1450N/A
1450N/A if (entry->hole_follows) {
1450N/A hole_start = drm_mm_hole_node_start(entry);
1450N/A hole_end = drm_mm_hole_node_end(entry);
1450N/A hole_size = hole_end - hole_start;
1450N/A DRM_DEBUG("%s 0x%08lx-0x%08lx: %8lu: free\n",
1450N/A prefix, hole_start, hole_end,
1450N/A hole_size);
1450N/A return hole_size;
1450N/A }
1450N/A
1450N/A return 0;
1450N/A}
1450N/A
1450N/Avoid drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
1450N/A{
1450N/A struct drm_mm_node *entry;
1450N/A unsigned long total_used = 0, total_free = 0, total = 0;
1450N/A
1450N/A total_free += drm_mm_debug_hole(&mm->head_node, prefix);
1450N/A
1450N/A drm_mm_for_each_node(entry, struct drm_mm_node, mm) {
1450N/A DRM_DEBUG("%s 0x%08lx-0x%08lx: %8lu: used\n",
1450N/A prefix, entry->start, entry->start + entry->size,
1450N/A entry->size);
1450N/A total_used += entry->size;
1450N/A total_free += drm_mm_debug_hole(entry, prefix);
1450N/A }
1450N/A total = total_free + total_used;
1450N/A
1450N/A DRM_DEBUG("%s total: %lu, used %lu free %lu\n", prefix, total,
1450N/A total_used, total_free);
1450N/A}
1450N/A
1450N/Abool drm_mm_initialized(struct drm_mm *mm)
1450N/A{
1450N/A return (mm->hole_stack.next != NULL);
1450N/A}
1450N/A
1450N/Aunsigned long __drm_mm_hole_node_start(struct drm_mm_node *hole_node)
1450N/A{
1450N/A return hole_node->start + hole_node->size;
1450N/A}
1450N/A
1450N/Aunsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node)
1450N/A{
1450N/A BUG_ON(!hole_node->hole_follows);
1450N/A return __drm_mm_hole_node_start(hole_node);
1450N/A}
1450N/A
1450N/Aunsigned long __drm_mm_hole_node_end(struct drm_mm_node *hole_node)
1450N/A{
1450N/A struct drm_mm_node *node;
1450N/A node = list_entry(hole_node->node_list.next,
1450N/A struct drm_mm_node, node_list);
1450N/A return node->start;
1450N/A}
1450N/A
1450N/Aunsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node)
1450N/A{
1450N/A return __drm_mm_hole_node_end(hole_node);
1450N/A}
1450N/A
1450N/Astruct drm_mm_node *drm_mm_get_block(struct drm_mm_node *parent,
1450N/A unsigned long size,
1450N/A unsigned alignment)
1450N/A{
1450N/A return drm_mm_get_block_generic(parent, size, alignment, 0, 0);
1450N/A}
1450N/Astruct drm_mm_node *drm_mm_get_block_atomic(struct drm_mm_node *parent,
1450N/A unsigned long size,
1450N/A unsigned alignment)
1450N/A{
1450N/A return drm_mm_get_block_generic(parent, size, alignment, 0, 1);
1450N/A}
1450N/Astruct drm_mm_node *drm_mm_get_block_range(
1450N/A struct drm_mm_node *parent,
1450N/A unsigned long size,
1450N/A unsigned alignment,
1450N/A unsigned long start,
1450N/A unsigned long end)
1450N/A{
1450N/A return drm_mm_get_block_range_generic(parent, size, alignment, 0,
1450N/A start, end, 0);
1450N/A}
1450N/Astruct drm_mm_node *drm_mm_get_block_atomic_range(
1450N/A struct drm_mm_node *parent,
1450N/A unsigned long size,
1450N/A unsigned alignment,
1450N/A unsigned long start,
1450N/A unsigned long end)
1450N/A{
1450N/A return drm_mm_get_block_range_generic(parent, size, alignment, 0,
1450N/A start, end, 1);
1450N/A}
1450N/A
1450N/Astruct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
1450N/A unsigned long size,
1450N/A unsigned alignment,
1450N/A bool best_match)
1450N/A{
1450N/A return drm_mm_search_free_generic(mm,size, alignment, 0, best_match);
1450N/A}
1450N/Astruct drm_mm_node *drm_mm_search_free_in_range(
1450N/A const struct drm_mm *mm,
1450N/A unsigned long size,
1450N/A unsigned alignment,
1450N/A unsigned long start,
1450N/A unsigned long end,
1450N/A bool best_match)
1450N/A{
1450N/A return drm_mm_search_free_in_range_generic(mm, size, alignment, 0,
1450N/A start, end, best_match);
1450N/A}