Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 496734bf authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/core: add support for reverse mm allocations



Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 5a5c7432
Loading
Loading
Loading
Loading
+122 −51
Original line number Original line Diff line number Diff line
/*
/*
 * Copyright 2010 Red Hat Inc.
 * Copyright 2012 Red Hat Inc.
 *
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * copy of this software and associated documentation files (the "Software"),
@@ -22,19 +22,52 @@
 * Authors: Ben Skeggs
 * Authors: Ben Skeggs
 */
 */


#include <core/os.h>
#include "core/os.h"
#include <core/mm.h>
#include "core/mm.h"


static inline void
#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
region_put(struct nouveau_mm *mm, struct nouveau_mm_node *a)
	list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)

void
nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis)
{
{
	list_del(&a->nl_entry);
	struct nouveau_mm_node *this = *pthis;
	list_del(&a->fl_entry);

	kfree(a);
	if (this) {
		struct nouveau_mm_node *prev = node(this, prev);
		struct nouveau_mm_node *next = node(this, next);

		if (prev && prev->type == 0) {
			prev->length += this->length;
			list_del(&this->nl_entry);
			kfree(this); this = prev;
		}

		if (next && next->type == 0) {
			next->offset  = this->offset;
			next->length += this->length;
			if (this->type == 0)
				list_del(&this->fl_entry);
			list_del(&this->nl_entry);
			kfree(this); this = NULL;
		}

		if (this && this->type != 0) {
			list_for_each_entry(prev, &mm->free, fl_entry) {
				if (this->offset < prev->offset)
					break;
			}

			list_add_tail(&this->fl_entry, &prev->fl_entry);
			this->type = 0;
		}
	}

	*pthis = NULL;
}
}


static struct nouveau_mm_node *
static struct nouveau_mm_node *
region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
region_head(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
{
{
	struct nouveau_mm_node *b;
	struct nouveau_mm_node *b;


@@ -56,63 +89,107 @@ region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
	return b;
	return b;
}
}


#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
int
	list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
nouveau_mm_head(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,

		u32 align, struct nouveau_mm_node **pnode)
void
nouveau_mm_put(struct nouveau_mm *mm, struct nouveau_mm_node *this)
{
{
	struct nouveau_mm_node *prev = node(this, prev);
	struct nouveau_mm_node *prev, *this, *next;
	struct nouveau_mm_node *next = node(this, next);
	u32 mask = align - 1;
	u32 splitoff;
	u32 s, e;


	list_add(&this->fl_entry, &mm->free);
	list_for_each_entry(this, &mm->free, fl_entry) {
	this->type = 0;
		e = this->offset + this->length;
		s = this->offset;


	if (prev && prev->type == 0) {
		prev = node(this, prev);
		prev->length += this->length;
		if (prev && prev->type != type)
		region_put(mm, this);
			s = roundup(s, mm->block_size);
		this = prev;

		next = node(this, next);
		if (next && next->type != type)
			e = rounddown(e, mm->block_size);

		s  = (s + mask) & ~mask;
		e &= ~mask;
		if (s > e || e - s < size_min)
			continue;

		splitoff = s - this->offset;
		if (splitoff && !region_head(mm, this, splitoff))
			return -ENOMEM;

		this = region_head(mm, this, min(size_max, e - s));
		if (!this)
			return -ENOMEM;

		this->type = type;
		list_del(&this->fl_entry);
		*pnode = this;
		return 0;
	}
	}


	if (next && next->type == 0) {
	return -ENOSPC;
		next->offset  = this->offset;
		next->length += this->length;
		region_put(mm, this);
}
}

static struct nouveau_mm_node *
region_tail(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
{
	struct nouveau_mm_node *b;

	if (a->length == size)
		return a;

	b = kmalloc(sizeof(*b), GFP_KERNEL);
	if (unlikely(b == NULL))
		return NULL;

	a->length -= size;
	b->offset  = a->offset + a->length;
	b->length  = size;
	b->type    = a->type;

	list_add(&b->nl_entry, &a->nl_entry);
	if (b->type == 0)
		list_add(&b->fl_entry, &a->fl_entry);
	return b;
}
}


int
int
nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc,
nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
		u32 align, struct nouveau_mm_node **pnode)
		u32 align, struct nouveau_mm_node **pnode)
{
{
	struct nouveau_mm_node *prev, *this, *next;
	struct nouveau_mm_node *prev, *this, *next;
	u32 min = size_nc ? size_nc : size;
	u32 mask = align - 1;
	u32 align_mask = align - 1;
	u32 splitoff;
	u32 s, e;


	list_for_each_entry(this, &mm->free, fl_entry) {
	list_for_each_entry_reverse(this, &mm->free, fl_entry) {
		e = this->offset + this->length;
		u32 e = this->offset + this->length;
		s = this->offset;
		u32 s = this->offset;
		u32 c = 0, a;


		prev = node(this, prev);
		prev = node(this, prev);
		if (prev && prev->type != type)
		if (prev && prev->type != type)
			s = roundup(s, mm->block_size);
			s = roundup(s, mm->block_size);


		next = node(this, next);
		next = node(this, next);
		if (next && next->type != type)
		if (next && next->type != type) {
			e = rounddown(e, mm->block_size);
			e = rounddown(e, mm->block_size);
			c = next->offset - e;
		}


		s  = (s + align_mask) & ~align_mask;
		s = (s + mask) & ~mask;
		e &= ~align_mask;
		a = e - s;
		if (s > e || e - s < min)
		if (s > e || a < size_min)
			continue;
			continue;


		splitoff = s - this->offset;
		a  = min(a, size_max);
		if (splitoff && !region_split(mm, this, splitoff))
		s  = (e - a) & ~mask;
		c += (e - s) - a;

		if (c && !region_tail(mm, this, c))
			return -ENOMEM;
			return -ENOMEM;


		this = region_split(mm, this, min(size, e - s));
		this = region_tail(mm, this, a);
		if (!this)
		if (!this)
			return -ENOMEM;
			return -ENOMEM;


@@ -147,6 +224,7 @@ nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
	list_add_tail(&node->nl_entry, &mm->nodes);
	list_add_tail(&node->nl_entry, &mm->nodes);
	list_add_tail(&node->fl_entry, &mm->free);
	list_add_tail(&node->fl_entry, &mm->free);
	mm->heap_nodes++;
	mm->heap_nodes++;
	mm->heap_size += length;
	return 0;
	return 0;
}
}


@@ -158,16 +236,9 @@ nouveau_mm_fini(struct nouveau_mm *mm)
	int nodes = 0;
	int nodes = 0;


	list_for_each_entry(node, &mm->nodes, nl_entry) {
	list_for_each_entry(node, &mm->nodes, nl_entry) {
		if (nodes++ == mm->heap_nodes) {
		if (nodes++ == mm->heap_nodes)
			printk(KERN_ERR "nouveau_mm in use at destroy time!\n");
			list_for_each_entry(node, &mm->nodes, nl_entry) {
				printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n",
				       node->type, node->offset, node->length);
			}
			WARN_ON(1);
			return -EBUSY;
			return -EBUSY;
	}
	}
	}


	kfree(heap);
	kfree(heap);
	return 0;
	return 0;
+6 −28
Original line number Original line Diff line number Diff line
/*
 * Copyright 2010 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: Ben Skeggs
 */

#ifndef __NOUVEAU_MM_H__
#ifndef __NOUVEAU_MM_H__
#define __NOUVEAU_MM_H__
#define __NOUVEAU_MM_H__


@@ -43,13 +19,15 @@ struct nouveau_mm {


	u32 block_size;
	u32 block_size;
	int heap_nodes;
	int heap_nodes;
	u32 heap_size;
};
};


int  nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block);
int  nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block);
int  nouveau_mm_fini(struct nouveau_mm *);
int  nouveau_mm_fini(struct nouveau_mm *);
int  nouveau_mm_pre(struct nouveau_mm *);
int  nouveau_mm_head(struct nouveau_mm *, u8 type, u32 size_max, u32 size_min,
int  nouveau_mm_get(struct nouveau_mm *, int type, u32 size, u32 size_nc,
		     u32 align, struct nouveau_mm_node **);
int  nouveau_mm_tail(struct nouveau_mm *, u8 type, u32 size_max, u32 size_min,
		     u32 align, struct nouveau_mm_node **);
		     u32 align, struct nouveau_mm_node **);
void nouveau_mm_put(struct nouveau_mm *, struct nouveau_mm_node *);
void nouveau_mm_free(struct nouveau_mm *, struct nouveau_mm_node **);


#endif
#endif
+12 −6
Original line number Original line Diff line number Diff line
@@ -65,7 +65,7 @@ nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem)
		this = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
		this = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);


		list_del(&this->rl_entry);
		list_del(&this->rl_entry);
		nouveau_mm_put(mm, this);
		nouveau_mm_free(mm, &this);
	}
	}


	if (mem->tag) {
	if (mem->tag) {
@@ -78,7 +78,7 @@ nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem)
}
}


int
int
nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc,
nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin,
	      u32 memtype, struct nouveau_mem **pmem)
	      u32 memtype, struct nouveau_mem **pmem)
{
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -87,13 +87,14 @@ nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc,
	struct nouveau_mem *mem;
	struct nouveau_mem *mem;
	int comp = (memtype & 0x300) >> 8;
	int comp = (memtype & 0x300) >> 8;
	int type = (memtype & 0x07f);
	int type = (memtype & 0x07f);
	int back = (memtype & 0x800);
	int ret;
	int ret;


	if (!types[type])
		return -EINVAL;
	size >>= 12;
	size >>= 12;
	align >>= 12;
	align >>= 12;
	size_nc >>= 12;
	ncmin >>= 12;
	if (!ncmin)
		ncmin = size;


	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
	if (!mem)
	if (!mem)
@@ -119,8 +120,13 @@ nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc,
	mem->memtype = (comp << 7) | type;
	mem->memtype = (comp << 7) | type;
	mem->size = size;
	mem->size = size;


	type = types[type];
	do {
	do {
		ret = nouveau_mm_get(mm, types[type], size, size_nc, align, &r);
		if (back)
			ret = nouveau_mm_tail(mm, type, size, ncmin, align, &r);
		else
			ret = nouveau_mm_head(mm, type, size, ncmin, align, &r);

		if (ret) {
		if (ret) {
			mutex_unlock(&mm->mutex);
			mutex_unlock(&mm->mutex);
			nv50_vram_del(dev, &mem);
			nv50_vram_del(dev, &mem);
+10 −3
Original line number Original line Diff line number Diff line
@@ -58,17 +58,21 @@ nvc0_vram_flags_valid(struct drm_device *dev, u32 tile_flags)


int
int
nvc0_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin,
nvc0_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin,
	      u32 type, struct nouveau_mem **pmem)
	      u32 memtype, struct nouveau_mem **pmem)
{
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
	struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
	struct nouveau_mm_node *r;
	struct nouveau_mm_node *r;
	struct nouveau_mem *mem;
	struct nouveau_mem *mem;
	int type = (memtype & 0x0ff);
	int back = (memtype & 0x800);
	int ret;
	int ret;


	size  >>= 12;
	size  >>= 12;
	align >>= 12;
	align >>= 12;
	ncmin >>= 12;
	ncmin >>= 12;
	if (!ncmin)
		ncmin = size;


	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
	if (!mem)
	if (!mem)
@@ -76,12 +80,15 @@ nvc0_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin,


	INIT_LIST_HEAD(&mem->regions);
	INIT_LIST_HEAD(&mem->regions);
	mem->dev = dev_priv->dev;
	mem->dev = dev_priv->dev;
	mem->memtype = (type & 0xff);
	mem->memtype = type;
	mem->size = size;
	mem->size = size;


	mutex_lock(&mm->mutex);
	mutex_lock(&mm->mutex);
	do {
	do {
		ret = nouveau_mm_get(mm, 1, size, ncmin, align, &r);
		if (back)
			ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r);
		else
			ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r);
		if (ret) {
		if (ret) {
			mutex_unlock(&mm->mutex);
			mutex_unlock(&mm->mutex);
			nv50_vram_del(dev, &mem);
			nv50_vram_del(dev, &mem);
+4 −5
Original line number Original line Diff line number Diff line
@@ -282,7 +282,8 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
	int ret;
	int ret;


	mutex_lock(&vm->mm.mutex);
	mutex_lock(&vm->mm.mutex);
	ret = nouveau_mm_get(&vm->mm, page_shift, msize, 0, align, &vma->node);
	ret = nouveau_mm_head(&vm->mm, page_shift, msize, msize, align,
			     &vma->node);
	if (unlikely(ret != 0)) {
	if (unlikely(ret != 0)) {
		mutex_unlock(&vm->mm.mutex);
		mutex_unlock(&vm->mm.mutex);
		return ret;
		return ret;
@@ -303,9 +304,8 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
		if (ret) {
		if (ret) {
			if (pde != fpde)
			if (pde != fpde)
				nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1);
				nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1);
			nouveau_mm_put(&vm->mm, vma->node);
			nouveau_mm_free(&vm->mm, &vma->node);
			mutex_unlock(&vm->mm.mutex);
			mutex_unlock(&vm->mm.mutex);
			vma->node = NULL;
			return ret;
			return ret;
		}
		}
	}
	}
@@ -330,8 +330,7 @@ nouveau_vm_put(struct nouveau_vma *vma)


	mutex_lock(&vm->mm.mutex);
	mutex_lock(&vm->mm.mutex);
	nouveau_vm_unmap_pgt(vm, vma->node->type != vm->spg_shift, fpde, lpde);
	nouveau_vm_unmap_pgt(vm, vma->node->type != vm->spg_shift, fpde, lpde);
	nouveau_mm_put(&vm->mm, vma->node);
	nouveau_mm_free(&vm->mm, &vma->node);
	vma->node = NULL;
	mutex_unlock(&vm->mm.mutex);
	mutex_unlock(&vm->mm.mutex);
}
}