diff options
author | Owain Ainsworth <oga@cvs.openbsd.org> | 2008-06-12 19:14:54 +0000 |
---|---|---|
committer | Owain Ainsworth <oga@cvs.openbsd.org> | 2008-06-12 19:14:54 +0000 |
commit | 016d76b42f5668a3ec87b0eaae5db4dbdd895ac0 (patch) | |
tree | 5634e51ee8865331a381cb90df6b8be150505a61 /sys/dev | |
parent | 3d3206644e64e89ec391bfc42c96961e1bb9b527 (diff) |
The mmap offsets for memory buffers currently are the kernel virtual
address. This is just plain wrong. scatter/gather on amd64 didn't work
here, since char device mmap doesn't take negative offsets so higher
kvas fail.
Instead, prematurely import drm_memrange which is needed for the memory
managers (GEM or TTM), and is used to manage GART space. Then, horribly
abuse it to allocate mmap offsets, fixes up the issues.
"just commit it" art@.
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/pci/drm/drmP.h | 45 | ||||
-rw-r--r-- | sys/dev/pci/drm/drm_bufs.c | 24 | ||||
-rw-r--r-- | sys/dev/pci/drm/drm_drv.c | 12 | ||||
-rw-r--r-- | sys/dev/pci/drm/drm_memrange.c | 270 | ||||
-rw-r--r-- | sys/dev/pci/drm/drm_scatter.c | 2 | ||||
-rw-r--r-- | sys/dev/pci/drm/drm_vm.c | 13 | ||||
-rw-r--r-- | sys/dev/pci/drm/files.drm | 3 |
7 files changed, 357 insertions, 12 deletions
diff --git a/sys/dev/pci/drm/drmP.h b/sys/dev/pci/drm/drmP.h index c9ffa6c4bfa..9186a79698f 100644 --- a/sys/dev/pci/drm/drmP.h +++ b/sys/dev/pci/drm/drmP.h @@ -186,6 +186,7 @@ typedef struct drm_file drm_file_t; #define DRM_MEM_STUB 18 #define DRM_MEM_SGLISTS 19 #define DRM_MEM_DRAWABLE 20 +#define DRM_MEM_MM 21 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) @@ -707,6 +708,26 @@ typedef struct drm_sg_mem { #endif } drm_sg_mem_t; +/* + * Generic memory range manager structs + */ + +struct drm_memrange_node { + TAILQ_ENTRY(drm_memrange_node) ml_entry; + TAILQ_ENTRY(drm_memrange_node) fl_entry; + struct drm_memrange *mm; + void *private; + int free; + unsigned long start; + unsigned long size; +}; + +TAILQ_HEAD(drm_mmq, drm_memrange_node); +struct drm_memrange { + struct drm_mmq ml; + struct drm_mmq fl; +}; + typedef TAILQ_HEAD(drm_map_list, drm_local_map) drm_map_list_t; typedef struct drm_local_map { @@ -728,6 +749,7 @@ typedef struct drm_local_map { bus_space_handle_t bsh; drm_dma_handle_t *dmah; TAILQ_ENTRY(drm_local_map) link; + struct drm_memrange_node *mm; } drm_local_map_t; TAILQ_HEAD(drm_vbl_sig_list, drm_vbl_sig); @@ -881,6 +903,7 @@ struct drm_device { SPLAY_HEAD(drm_magic_tree, drm_magic_entry) magiclist; /* Linked list of mappable regions. Protected by dev_lock */ + struct drm_memrange handle_mm; drm_map_list_t maplist; drm_local_map_t **context_sareas; @@ -1040,6 +1063,8 @@ unsigned long drm_get_resource_start(drm_device_t *dev, unsigned int resource); unsigned long drm_get_resource_len(drm_device_t *dev, unsigned int resource); void drm_rmmap(drm_device_t *dev, drm_local_map_t *map); int drm_order(unsigned long size); +drm_local_map_t + *drm_find_matching_map(struct drm_device *, drm_local_map_t *); int drm_addmap(drm_device_t * dev, unsigned long offset, unsigned long size, drm_map_type_t type, drm_map_flags_t flags, drm_local_map_t **map_ptr); @@ -1178,6 +1203,24 @@ drm_dma_handle_t *drm_pci_alloc(drm_device_t *dev, size_t size, size_t align, dma_addr_t maxaddr); void drm_pci_free(drm_device_t *dev, drm_dma_handle_t *dmah); +/* Memrange functions for managing aperture space */ +struct drm_memrange_node + *drm_memrange_get_block(struct drm_memrange_node *, + unsigned long, unsigned); +void drm_memrange_put_block(struct drm_memrange_node *); +struct drm_memrange_node + *drm_memrange_search_free(const struct drm_memrange *, + unsigned long, unsigned, int); +int drm_memrange_init(struct drm_memrange *, unsigned long, + unsigned long); +void drm_memrange_takedown(struct drm_memrange *); +int drm_memrange_clean(struct drm_memrange *); +unsigned long drm_memrange_tail_space(struct drm_memrange *); +int drm_memrange_remove_space_from_tail(struct drm_memrange *, + unsigned long); +int drm_memrange_add_space_to_tail(struct drm_memrange *, + unsigned long ); + /* Inline replacements for DRM_IOREMAP macros */ static __inline__ void drm_core_ioremap(struct drm_local_map *map, struct drm_device *dev) { @@ -1195,7 +1238,7 @@ static __inline__ struct drm_local_map *drm_core_findmap(struct drm_device *dev, DRM_SPINLOCK_ASSERT(&dev->dev_lock); TAILQ_FOREACH(map, &dev->maplist, link) { - if (map->offset == offset) + if (offset == map->mm->start) return map; } return NULL; diff --git a/sys/dev/pci/drm/drm_bufs.c b/sys/dev/pci/drm/drm_bufs.c index 83e0c71f1ae..ae3a50fb351 100644 --- a/sys/dev/pci/drm/drm_bufs.c +++ b/sys/dev/pci/drm/drm_bufs.c @@ -191,6 +191,22 @@ drm_addmap(drm_device_t * dev, unsigned long offset, unsigned long size, map->type = type; map->flags = flags; + + map->mm = drm_memrange_search_free(&dev->handle_mm, map->size, + PAGE_SIZE, 0); + if (map->mm == NULL) { + DRM_ERROR("can't find free offset\n"); + free(map, M_DRM); + return ENOMEM; + } + map->mm = drm_memrange_get_block(map->mm, map->size, + PAGE_SIZE); + if (map->mm == NULL) { + DRM_ERROR("can't get block\n"); + free(map, M_DRM); + return ENOMEM; + } + switch ( map->type ) { case _DRM_REGISTERS: map->handle = drm_ioremap(dev, map); @@ -365,7 +381,7 @@ drm_addmap_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv) #ifndef __OpenBSD__ if (request->type != _DRM_SHM) #endif - request->handle = (void *)request->offset; + request->handle = (void *)map->mm->start; return 0; } @@ -421,6 +437,8 @@ drm_rmmap(drm_device_t *dev, drm_local_map_t *map) } #endif + drm_memrange_put_block(map->mm); + free(map, M_DRM); } @@ -676,7 +694,7 @@ drm_do_addbufs_pci(drm_device_t *dev, drm_buf_desc_t *request) free(entry->buflist, M_DRM); return ENOMEM; } - + memcpy(temp_pagelist, dma->pagelist, dma->page_count * sizeof(*dma->pagelist)); @@ -1184,7 +1202,7 @@ drm_mapbufs(drm_device_t *dev, void *data, struct drm_file *file_priv) goto done; } size = round_page(map->size); - foff = map->offset; + foff = map->mm->start; } else { size = round_page(dma->byte_count), foff = 0; diff --git a/sys/dev/pci/drm/drm_drv.c b/sys/dev/pci/drm/drm_drv.c index efb5e16b695..8b11eb8145d 100644 --- a/sys/dev/pci/drm/drm_drv.c +++ b/sys/dev/pci/drm/drm_drv.c @@ -517,6 +517,16 @@ drm_load(drm_device_t *dev) #endif TAILQ_INIT(&dev->files); + /* + * the dma buffers api is just weird. offset 1Gb to ensure we don't + * conflict with it. + */ + retcode = drm_memrange_init(&dev->handle_mm, 1024*1024*1024, LONG_MAX); + if (retcode != 0) { + DRM_ERROR("Failed to initialise handle memrange\n"); + goto error; + } + if (dev->driver.load != NULL) { DRM_LOCK(); /* Shared code returns -errno. */ @@ -593,6 +603,8 @@ drm_unload(drm_device_t *dev) drm_ctxbitmap_cleanup(dev); + drm_memrange_takedown(&dev->handle_mm); + #if !defined(DRM_NO_MTRR) && !defined(DRM_NO_AGP) if (dev->agp && dev->agp->mtrr) { int retcode; diff --git a/sys/dev/pci/drm/drm_memrange.c b/sys/dev/pci/drm/drm_memrange.c new file mode 100644 index 00000000000..5b4a9658aa4 --- /dev/null +++ b/sys/dev/pci/drm/drm_memrange.c @@ -0,0 +1,270 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. + * + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + * + **************************************************************************/ + +/* + * Generic simple memory manager implementation. Intended to be used as a base + * class implementation for more advanced memory managers. + * + * Note that the algorithm used is quite simple and there might be substantial + * performance gains if a smarter free list is implemented. Currently it is just an + * unordered stack of free regions. This could easily be improved if an RB-tree + * is used instead. At least if we expect heavy fragmentation. + * + * Aligned allocations can also see improvement. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + * Ported to OpenBSD by: + * Owain Ainsworth <oga@openbsd.org> + */ + +#include "drmP.h" + +int drm_memrange_create_tail_node(struct drm_memrange *, unsigned long, unsigned long); +struct drm_memrange_node *drm_memrange_split_at_start(struct + drm_memrange_node *, unsigned long); + +unsigned long +drm_memrange_tail_space(struct drm_memrange *mm) +{ + struct drm_memrange_node *entry = TAILQ_LAST(&mm->ml, drm_mmq); + + if (!entry->free) + return (0); + + return (entry->size); +} + +int +drm_memrange_remove_space_from_tail(struct drm_memrange *mm, unsigned long size) +{ + struct drm_memrange_node *entry = TAILQ_LAST(&mm->ml, drm_mmq); + + if (!entry->free) + return (ENOMEM); + + if (entry->size <= size) + return (ENOMEM); + + entry->size -=size; + return (0); +} + +int +drm_memrange_create_tail_node(struct drm_memrange *mm, unsigned long start, + unsigned long size) +{ + struct drm_memrange_node *child; + + child = (struct drm_memrange_node *)drm_calloc(1, sizeof(*child), + DRM_MEM_MM); + if (child == NULL) + return (ENOMEM); + + child->free = 1; + child->size = size; + child->start = start; + child->mm = mm; + + TAILQ_INSERT_TAIL(&mm->ml, child, ml_entry); + TAILQ_INSERT_TAIL(&mm->fl, child, fl_entry); + + return (0); +} + +int +drm_memrange_add_space_to_tail(struct drm_memrange *mm, unsigned long size) +{ + struct drm_memrange_node *entry = TAILQ_LAST(&mm->ml, drm_mmq); + + if (!entry->free) + return (drm_memrange_create_tail_node(mm, + entry->start + entry->size, size)); + entry->size += size; + return (0); + + +} + +struct drm_memrange_node * +drm_memrange_split_at_start(struct drm_memrange_node *parent, unsigned long size) +{ + struct drm_memrange_node *child; + + child = (struct drm_memrange_node *)drm_calloc(1, sizeof(*child), + DRM_MEM_MM); + if (child == NULL) + return (NULL); + + child->size = size; + child->start = parent->start; + child->mm = parent->mm; + + TAILQ_INSERT_TAIL(&child->mm->ml, child, ml_entry); + + parent->size -= size; + parent->start += size; + return (child); +} + +struct drm_memrange_node * +drm_memrange_get_block(struct drm_memrange_node *parent, unsigned long size, + unsigned alignment) +{ + struct drm_memrange_node *child, *align_splitoff = NULL; + unsigned tmp = 0; + + if (alignment) + tmp = parent->start & alignment; + + if (tmp) { + align_splitoff = drm_memrange_split_at_start(parent, + alignment - tmp); + if (align_splitoff == NULL) + return (NULL); + } + + if (parent->size == size) { + TAILQ_REMOVE(&parent->mm->fl, parent, fl_entry); + parent->free = 0; + return (parent); + } else + child = drm_memrange_split_at_start(parent, size); + + if (align_splitoff) + drm_memrange_put_block(align_splitoff); + + return (child); + +} + + +void +drm_memrange_put_block(struct drm_memrange_node *cur) +{ + struct drm_memrange *mm = cur->mm; + struct drm_memrange_node *prev_node = NULL; + struct drm_memrange_node *next_node; + int merged = 0; + + if ((prev_node = TAILQ_PREV(cur, drm_mmq, ml_entry)) != NULL) { + if (prev_node->free) { + prev_node->size += cur->size; + merged = 1; + } + } + if ((next_node = TAILQ_NEXT(cur, ml_entry)) != NULL) { + if (next_node ->free) { + if (merged) { + prev_node->size += next_node->size; + TAILQ_REMOVE(&mm->ml, next_node, + ml_entry); + TAILQ_REMOVE(&mm->fl, next_node, + fl_entry); + drm_free(next_node, sizeof(*next_node), + DRM_MEM_MM); + } + } + } + if (!merged) { + cur->free = 1; + TAILQ_INSERT_TAIL(&mm->fl, cur, fl_entry); + } else { + TAILQ_REMOVE(&mm->ml, cur, ml_entry); + drm_free(cur, sizeof(*cur), DRM_MEM_MM); + } +} + +struct drm_memrange_node * +drm_memrange_search_free(const struct drm_memrange *mm, unsigned long size, + unsigned alignment, int best_match) +{ + struct drm_memrange_node *entry, *best; + unsigned long best_size; + unsigned wasted; + + best = NULL; + best_size = ~0UL; + + TAILQ_FOREACH(entry, &mm->fl, fl_entry ) { + wasted = 0; + + if (entry->size < size) + continue; + + if (alignment) { + unsigned tmp = entry->start % alignment; + if (tmp) + wasted += alignment - tmp; + } + + if (entry->size >= size + wasted) { + if (!best_match) { + return (entry); + } + if (size < best_size) { + best = entry; + best_size = entry->size; + } + } + } + return (best); +} + +int +drm_memrange_clean(struct drm_memrange *mm) +{ + return (TAILQ_FIRST(&mm->ml) == TAILQ_LAST(&mm->ml,drm_mmq)); +} + +int +drm_memrange_init(struct drm_memrange *mm, unsigned long start, + unsigned long size) +{ + TAILQ_INIT(&mm->ml); + TAILQ_INIT(&mm->fl); + + return (drm_memrange_create_tail_node(mm, start, size)); +} + +void +drm_memrange_takedown(struct drm_memrange *mm) +{ + struct drm_memrange_node *entry; + + entry = TAILQ_FIRST(&mm->ml); + + if (!TAILQ_EMPTY(&mm->ml) || !TAILQ_EMPTY(&mm->fl)) { + DRM_ERROR("Memory manager not clean, Delaying takedown\n"); + return; + } + + TAILQ_INIT(&mm->ml); + TAILQ_INIT(&mm->fl); + drm_free(entry, sizeof(*entry), DRM_MEM_MM); +} diff --git a/sys/dev/pci/drm/drm_scatter.c b/sys/dev/pci/drm/drm_scatter.c index 3f9a691b610..705604c46a0 100644 --- a/sys/dev/pci/drm/drm_scatter.c +++ b/sys/dev/pci/drm/drm_scatter.c @@ -166,8 +166,6 @@ drm_sg_dmamem_alloc(drm_device_t *dev, size_t pages) bus_size_t size = pages << PAGE_SHIFT; int ret = 0; - printf("size = %d\n", size); - dsd = malloc(sizeof(*dsd), M_DRM, M_NOWAIT | M_ZERO); if (dsd == NULL) return (NULL); diff --git a/sys/dev/pci/drm/drm_vm.c b/sys/dev/pci/drm/drm_vm.c index 427a549fb5b..3e03157c537 100644 --- a/sys/dev/pci/drm/drm_vm.c +++ b/sys/dev/pci/drm/drm_vm.c @@ -98,8 +98,11 @@ drm_mmap(dev_t kdev, off_t offset, int prot) */ DRM_LOCK(); TAILQ_FOREACH(map, &dev->maplist, link) { - if (offset >= map->offset && offset < map->offset + map->size) + if (offset >= map->mm->start && + offset < map->mm->start + map->size) { + offset -= map->mm->start; break; + } } if (map == NULL) { @@ -123,7 +126,7 @@ drm_mmap(dev_t kdev, off_t offset, int prot) case _DRM_FRAME_BUFFER: case _DRM_REGISTERS: case _DRM_AGP: - phys = offset; + phys = offset + map->offset; break; #ifdef __FreeBSD__ case _DRM_CONSISTENT: @@ -137,12 +140,12 @@ drm_mmap(dev_t kdev, off_t offset, int prot) /* XXX unify all the bus_dmamem_mmap bits */ case _DRM_SCATTER_GATHER: return bus_dmamem_mmap(dev->pa.pa_dmat, dev->sg->mem->sg_segs, - dev->sg->mem->sg_nsegs, offset - dev->sg->handle, prot, - BUS_DMA_NOWAIT); + dev->sg->mem->sg_nsegs, map->offset - dev->sg->handle + + offset, prot, BUS_DMA_NOWAIT); case _DRM_SHM: case _DRM_CONSISTENT: return bus_dmamem_mmap(dev->pa.pa_dmat, &map->dmah->seg, 1, - offset - map->offset, prot, BUS_DMA_NOWAIT); + offset, prot, BUS_DMA_NOWAIT); #endif default: DRM_ERROR("bad map type %d\n", type); diff --git a/sys/dev/pci/drm/files.drm b/sys/dev/pci/drm/files.drm index 8e7f99e492c..1032f492cef 100644 --- a/sys/dev/pci/drm/files.drm +++ b/sys/dev/pci/drm/files.drm @@ -1,5 +1,5 @@ # $NetBSD: files.drm,v 1.2 2007/03/28 11:29:37 jmcneill Exp $ -# $OpenBSD: files.drm,v 1.1 2007/11/28 23:56:04 deraadt Exp $ +# $OpenBSD: files.drm,v 1.2 2008/06/12 19:14:53 oga Exp $ # direct rendering modules define drmbase @@ -15,6 +15,7 @@ file dev/pci/drm/drm_ioctl.c drmbase file dev/pci/drm/drm_irq.c drmbase file dev/pci/drm/drm_lock.c drmbase file dev/pci/drm/drm_memory.c drmbase +file dev/pci/drm/drm_memrange.c drmbase file dev/pci/drm/drm_pci.c drmbase file dev/pci/drm/drm_scatter.c drmbase file dev/pci/drm/drm_sysctl.c drmbase |