mirror of
https://gitlab.freedesktop.org/mesa/drm.git
synced 2025-12-25 23:30:10 +01:00
hook up memory manager ioctls
This commit is contained in:
parent
e45c84ba91
commit
2545b3dd43
9 changed files with 483 additions and 108 deletions
|
|
@ -364,6 +364,9 @@ typedef struct {
|
|||
#define DRM_IOCTL_RADEON_CMDBUF DRM_IOW( 0x50, drm_radeon_cmd_buffer_t)
|
||||
#define DRM_IOCTL_RADEON_GETPARAM DRM_IOWR(0x51, drm_radeon_getparam_t)
|
||||
#define DRM_IOCTL_RADEON_FLIP DRM_IO( 0x52)
|
||||
#define DRM_IOCTL_RADEON_ALLOC DRM_IOWR( 0x53, drm_radeon_mem_alloc_t)
|
||||
#define DRM_IOCTL_RADEON_FREE DRM_IOW( 0x54, drm_radeon_mem_free_t)
|
||||
#define DRM_IOCTL_RADEON_INIT_HEAP DRM_IOW( 0x55, drm_radeon_mem_init_heap_t)
|
||||
|
||||
typedef struct drm_radeon_init {
|
||||
enum {
|
||||
|
|
@ -509,4 +512,25 @@ typedef struct drm_radeon_getparam {
|
|||
int *value;
|
||||
} drm_radeon_getparam_t;
|
||||
|
||||
#define RADEON_MEM_REGION_AGP 1
|
||||
#define RADEON_MEM_REGION_FB 2
|
||||
|
||||
typedef struct drm_radeon_mem_alloc {
|
||||
int region;
|
||||
int alignment;
|
||||
int size;
|
||||
int *region_offset; /* offset from start of fb or agp */
|
||||
} drm_radeon_mem_alloc_t;
|
||||
|
||||
typedef struct drm_radeon_mem_free {
|
||||
int region;
|
||||
int region_offset;
|
||||
} drm_radeon_mem_free_t;
|
||||
|
||||
typedef struct drm_radeon_mem_init_heap {
|
||||
int region;
|
||||
int size;
|
||||
int start;
|
||||
} drm_radeon_mem_init_heap_t;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -61,6 +61,15 @@ typedef struct drm_radeon_depth_clear_t {
|
|||
u32 se_cntl;
|
||||
} drm_radeon_depth_clear_t;
|
||||
|
||||
|
||||
struct mem_block {
|
||||
struct mem_block *next;
|
||||
struct mem_block *prev;
|
||||
int start;
|
||||
int size;
|
||||
int pid; /* 0: free, -1: heap, other: real pids */
|
||||
};
|
||||
|
||||
typedef struct drm_radeon_private {
|
||||
drm_radeon_ring_buffer_t ring;
|
||||
drm_radeon_sarea_t *sarea_priv;
|
||||
|
|
@ -126,6 +135,9 @@ typedef struct drm_radeon_private {
|
|||
drm_map_t *ring_rptr;
|
||||
drm_map_t *buffers;
|
||||
drm_map_t *agp_textures;
|
||||
|
||||
struct mem_block *agp_heap;
|
||||
struct mem_block *fb_heap;
|
||||
} drm_radeon_private_t;
|
||||
|
||||
typedef struct drm_radeon_buf_priv {
|
||||
|
|
@ -164,6 +176,13 @@ extern int radeon_cp_cmdbuf( DRM_IOCTL_ARGS );
|
|||
extern int radeon_cp_getparam( DRM_IOCTL_ARGS );
|
||||
extern int radeon_cp_flip( DRM_IOCTL_ARGS );
|
||||
|
||||
extern int radeon_mem_alloc( DRM_IOCTL_ARGS );
|
||||
extern int radeon_mem_free( DRM_IOCTL_ARGS );
|
||||
extern int radeon_mem_init_heap( DRM_IOCTL_ARGS );
|
||||
extern void radeon_mem_takedown( struct mem_block **heap );
|
||||
extern void radeon_mem_release( struct mem_block *heap );
|
||||
|
||||
|
||||
/* Flags for stats.boxes
|
||||
*/
|
||||
#define RADEON_BOX_DMA_IDLE 0x1
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
/* radeon_state.c -- State support for Radeon -*- linux-c -*-
|
||||
/* radeon_mem.c -- Simple agp/fb memory manager for radeon -*- linux-c -*-
|
||||
*
|
||||
* Copyright 2000 VA Linux Systems, Inc., Fremont, California.
|
||||
* All Rights Reserved.
|
||||
* Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved.
|
||||
*
|
||||
* The Weather Channel (TM) funded Tungsten Graphics to develop the
|
||||
* initial release of the Radeon 8500 driver under the XFree86 license.
|
||||
* This notice must be preserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
|
|
@ -34,44 +37,15 @@
|
|||
|
||||
/* Very simple allocator for agp memory, working on a static range
|
||||
* already mapped into each client's address space.
|
||||
*
|
||||
* TODO: dynamically grow region, make regions truely private to each
|
||||
* client, etc. Make work with FB memory as well as agp.
|
||||
*/
|
||||
struct mem_block {
|
||||
struct mem_block *next;
|
||||
struct mem_block *prev;
|
||||
int start;
|
||||
int size;
|
||||
int pid; /* 0: free, -1: heap, other: real pids */
|
||||
};
|
||||
|
||||
|
||||
static int init(struct mem_block *heap, int start, int size)
|
||||
{
|
||||
struct mem_block *blocks = malloc(sizeof(*blocks));
|
||||
|
||||
if (!blocks)
|
||||
return -ENOMEM;
|
||||
|
||||
blocks->start = start;
|
||||
blocks->size = size;
|
||||
blocks->pid = 0;
|
||||
blocks->next = blocks->prev = heap;
|
||||
|
||||
memset( heap, 0, sizeof(*heap) );
|
||||
heap->pid = -1;
|
||||
heap->next = heap->prev = blocks;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct mem_block *split_block(struct mem_block *p, int start, int size,
|
||||
int pid )
|
||||
{
|
||||
/* Maybe cut off the start of an existing block */
|
||||
if (start > p->start) {
|
||||
struct mem_block *newblock = malloc(sizeof(*newblock));
|
||||
struct mem_block *newblock = kmalloc(sizeof(*newblock),
|
||||
GFP_KERNEL);
|
||||
if (!newblock)
|
||||
goto out;
|
||||
newblock->start = start;
|
||||
|
|
@ -87,7 +61,8 @@ static struct mem_block *split_block(struct mem_block *p, int start, int size,
|
|||
|
||||
/* Maybe cut off the end of an existing block */
|
||||
if (size < p->size) {
|
||||
struct mem_block *newblock = malloc(sizeof(*newblock));
|
||||
struct mem_block *newblock = kmalloc(sizeof(*newblock),
|
||||
GFP_KERNEL);
|
||||
if (!newblock)
|
||||
goto out;
|
||||
newblock->start = start + size;
|
||||
|
|
@ -113,18 +88,27 @@ static struct mem_block *alloc_block( struct mem_block *heap, int size,
|
|||
int mask = (1 << align2)-1;
|
||||
|
||||
for (p = heap->next ; p != heap ; p = p->next) {
|
||||
if (p->pid == 0) {
|
||||
int start = (p->start + mask) & ~mask;
|
||||
if (start + size <= p->start + p->size)
|
||||
return split_block( p, start, size, pid );
|
||||
}
|
||||
int start = (p->start + mask) & ~mask;
|
||||
if (p->pid == 0 && start + size <= p->start + p->size)
|
||||
return split_block( p, start, size, pid );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct mem_block *find_block( struct mem_block *heap, int start )
|
||||
{
|
||||
struct mem_block *p;
|
||||
|
||||
static void free_block( struct mem_block *b )
|
||||
for (p = heap->next ; p != heap ; p = p->next)
|
||||
if (p->start == start)
|
||||
return p;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void free_block( struct mem_block *p )
|
||||
{
|
||||
p->pid = 0;
|
||||
|
||||
|
|
@ -136,7 +120,7 @@ static void free_block( struct mem_block *b )
|
|||
p->size += q->size;
|
||||
p->next = q->next;
|
||||
p->next->prev = p;
|
||||
free(p);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
if (p->prev->pid == 0) {
|
||||
|
|
@ -144,12 +128,44 @@ static void free_block( struct mem_block *b )
|
|||
q->size += p->size;
|
||||
q->next = p->next;
|
||||
q->next->prev = q;
|
||||
free(p);
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_by_pid( struct mem_block *heap, int pid )
|
||||
/* Initialize. How to check for an uninitialized heap?
|
||||
*/
|
||||
static int init_heap(struct mem_block **heap, int start, int size)
|
||||
{
|
||||
struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL);
|
||||
|
||||
if (!blocks)
|
||||
return -ENOMEM;
|
||||
|
||||
*heap = kmalloc(sizeof(**heap), GFP_KERNEL);
|
||||
if (!*heap) {
|
||||
kfree( blocks );
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
blocks->start = start;
|
||||
blocks->size = size;
|
||||
blocks->pid = 0;
|
||||
blocks->next = blocks->prev = *heap;
|
||||
|
||||
memset( *heap, 0, sizeof(**heap) );
|
||||
(*heap)->pid = -1;
|
||||
(*heap)->next = (*heap)->prev = blocks;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Free all blocks associated with the releasing pid.
|
||||
*/
|
||||
void radeon_mem_release( struct mem_block *heap )
|
||||
{
|
||||
int pid = DRM_CURRENTPID;
|
||||
struct mem_block *p;
|
||||
|
||||
for (p = heap->next ; p != heap ; p = p->next) {
|
||||
if (p->pid == pid)
|
||||
p->pid = 0;
|
||||
|
|
@ -164,21 +180,138 @@ static void free_by_pid( struct mem_block *heap, int pid )
|
|||
p->size += q->size;
|
||||
p->next = q->next;
|
||||
p->next->prev = p;
|
||||
free(q);
|
||||
kfree(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void destroy(struct mem_block *heap)
|
||||
/* Shutdown.
|
||||
*/
|
||||
void radeon_mem_takedown( struct mem_block **heap )
|
||||
{
|
||||
struct mem_block *heap;
|
||||
struct mem_block *p;
|
||||
|
||||
for (p = heap->next ; p != heap ; ) {
|
||||
for (p = (*heap)->next ; p != *heap ; ) {
|
||||
struct mem_block *q = p;
|
||||
p = p->next;
|
||||
free(q);
|
||||
kfree(q);
|
||||
}
|
||||
|
||||
heap->next = heap->prev = heap;
|
||||
kfree( *heap );
|
||||
*heap = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* IOCTL HANDLERS */
|
||||
|
||||
static struct mem_block **get_heap( drm_radeon_private_t *dev_priv,
|
||||
int region )
|
||||
{
|
||||
switch( region ) {
|
||||
case RADEON_MEM_REGION_AGP:
|
||||
return &dev_priv->fb_heap;
|
||||
case RADEON_MEM_REGION_FB:
|
||||
return &dev_priv->agp_heap;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int radeon_mem_alloc( DRM_IOCTL_ARGS )
|
||||
{
|
||||
DRM_DEVICE;
|
||||
drm_radeon_private_t *dev_priv = dev->dev_private;
|
||||
drm_radeon_mem_alloc_t alloc;
|
||||
struct mem_block *block, **heap;
|
||||
|
||||
if ( !dev_priv ) {
|
||||
DRM_ERROR( "%s called with no initialization\n", __func__ );
|
||||
return DRM_ERR(EINVAL);
|
||||
}
|
||||
|
||||
DRM_COPY_FROM_USER_IOCTL( alloc, (drm_radeon_mem_alloc_t *)data,
|
||||
sizeof(alloc) );
|
||||
|
||||
heap = get_heap( dev_priv, alloc.region );
|
||||
if (!heap || !*heap)
|
||||
return DRM_ERR(EFAULT);
|
||||
|
||||
/* Make things easier on ourselves: all allocations at least
|
||||
* 4k aligned.
|
||||
*/
|
||||
if (alloc.alignment < 12)
|
||||
alloc.alignment = 12;
|
||||
|
||||
block = alloc_block( *heap, alloc.size, alloc.alignment,
|
||||
DRM_CURRENTPID );
|
||||
|
||||
if ( DRM_COPY_TO_USER( alloc.region_offset, &block->start,
|
||||
sizeof(int) ) ) {
|
||||
DRM_ERROR( "copy_to_user\n" );
|
||||
return DRM_ERR(EFAULT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int radeon_mem_free( DRM_IOCTL_ARGS )
|
||||
{
|
||||
DRM_DEVICE;
|
||||
drm_radeon_private_t *dev_priv = dev->dev_private;
|
||||
drm_radeon_mem_free_t memfree;
|
||||
struct mem_block *block, **heap;
|
||||
|
||||
if ( !dev_priv ) {
|
||||
DRM_ERROR( "%s called with no initialization\n", __func__ );
|
||||
return DRM_ERR(EINVAL);
|
||||
}
|
||||
|
||||
DRM_COPY_FROM_USER_IOCTL( memfree, (drm_radeon_mem_free_t *)data,
|
||||
sizeof(memfree) );
|
||||
|
||||
heap = get_heap( dev_priv, memfree.region );
|
||||
if (!heap || !*heap)
|
||||
return DRM_ERR(EFAULT);
|
||||
|
||||
block = find_block( *heap, memfree.region_offset );
|
||||
if (!block)
|
||||
return DRM_ERR(EFAULT);
|
||||
|
||||
if (block->pid != DRM_CURRENTPID)
|
||||
return DRM_ERR(EPERM);
|
||||
|
||||
free_block( block );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int radeon_mem_init_heap( DRM_IOCTL_ARGS )
|
||||
{
|
||||
DRM_DEVICE;
|
||||
drm_radeon_private_t *dev_priv = dev->dev_private;
|
||||
drm_radeon_mem_init_heap_t initheap;
|
||||
struct mem_block **heap;
|
||||
|
||||
if ( !dev_priv ) {
|
||||
DRM_ERROR( "%s called with no initialization\n", __func__ );
|
||||
return DRM_ERR(EINVAL);
|
||||
}
|
||||
|
||||
DRM_COPY_FROM_USER_IOCTL( initheap, (drm_radeon_mem_init_heap_t *)data,
|
||||
sizeof(initheap) );
|
||||
|
||||
heap = get_heap( dev_priv, initheap.region );
|
||||
if (!heap)
|
||||
return DRM_ERR(EFAULT);
|
||||
|
||||
if (*heap) {
|
||||
DRM_ERROR("heap already initialized?");
|
||||
return DRM_ERR(EFAULT);
|
||||
}
|
||||
|
||||
return init_heap( heap, initheap.start, initheap.size );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1306,6 +1306,9 @@ static int radeon_do_init_pageflip( drm_device_t *dev )
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Called whenever a client dies, from DRM(release).
|
||||
* NOTE: Lock isn't necessarily held when this is called!
|
||||
*/
|
||||
int radeon_do_cleanup_pageflip( drm_device_t *dev )
|
||||
{
|
||||
drm_radeon_private_t *dev_priv = dev->dev_private;
|
||||
|
|
|
|||
|
|
@ -48,10 +48,10 @@
|
|||
|
||||
#define DRIVER_NAME "radeon"
|
||||
#define DRIVER_DESC "ATI Radeon"
|
||||
#define DRIVER_DATE "20020611"
|
||||
#define DRIVER_DATE "20020828"
|
||||
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 5
|
||||
#define DRIVER_MINOR 6
|
||||
#define DRIVER_PATCHLEVEL 0
|
||||
|
||||
/* Interface history:
|
||||
|
|
@ -68,6 +68,7 @@
|
|||
* 1.5 - Add r200 packets to cmdbuf ioctl
|
||||
* - Add r200 function to init ioctl
|
||||
* - Add 'scalar2' instruction to cmdbuf
|
||||
* 1.6 - Add static agp memory manager
|
||||
*/
|
||||
#define DRIVER_IOCTLS \
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { radeon_cp_buffers, 1, 0 }, \
|
||||
|
|
@ -88,21 +89,37 @@
|
|||
[DRM_IOCTL_NR(DRM_IOCTL_RADEON_VERTEX2)] = { radeon_cp_vertex2, 1, 0 }, \
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CMDBUF)] = { radeon_cp_cmdbuf, 1, 0 }, \
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_RADEON_GETPARAM)] = { radeon_cp_getparam, 1, 0 }, \
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_RADEON_FLIP)] = { radeon_cp_flip, 1, 0 },
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_RADEON_FLIP)] = { radeon_cp_flip, 1, 0 }, \
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_RADEON_ALLOC)] = { radeon_mem_alloc, 1, 0 }, \
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_RADEON_FREE)] = { radeon_mem_free, 1, 0 }, \
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_RADEON_INIT_HEAP)] = { radeon_mem_init_heap, 1, 1 },
|
||||
|
||||
/* Driver customization:
|
||||
/* When a client dies:
|
||||
* - Check for and clean up flipped page state
|
||||
* - Free any alloced agp memory.
|
||||
*
|
||||
* DRM infrastructure takes care of reclaiming dma buffers.
|
||||
*/
|
||||
#define DRIVER_PRERELEASE() do { \
|
||||
#define DRIVER_RELEASE() do { \
|
||||
if ( dev->dev_private ) { \
|
||||
drm_radeon_private_t *dev_priv = dev->dev_private; \
|
||||
if ( dev_priv->page_flipping ) { \
|
||||
radeon_do_cleanup_pageflip( dev ); \
|
||||
} \
|
||||
radeon_mem_release( dev_priv->agp_heap ); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* On unloading the module:
|
||||
* - Free memory heap structure
|
||||
* - Remove mappings made at startup and free dev_private.
|
||||
*/
|
||||
#define DRIVER_PRETAKEDOWN() do { \
|
||||
if ( dev->dev_private ) radeon_do_cleanup_cp( dev ); \
|
||||
if ( dev->dev_private ) { \
|
||||
drm_radeon_private_t *dev_priv = dev->dev_private; \
|
||||
radeon_mem_takedown( &(dev_priv->agp_heap) ); \
|
||||
radeon_do_cleanup_cp( dev ); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* DMA customization:
|
||||
|
|
|
|||
|
|
@ -364,6 +364,9 @@ typedef struct {
|
|||
#define DRM_IOCTL_RADEON_CMDBUF DRM_IOW( 0x50, drm_radeon_cmd_buffer_t)
|
||||
#define DRM_IOCTL_RADEON_GETPARAM DRM_IOWR(0x51, drm_radeon_getparam_t)
|
||||
#define DRM_IOCTL_RADEON_FLIP DRM_IO( 0x52)
|
||||
#define DRM_IOCTL_RADEON_ALLOC DRM_IOWR( 0x53, drm_radeon_mem_alloc_t)
|
||||
#define DRM_IOCTL_RADEON_FREE DRM_IOW( 0x54, drm_radeon_mem_free_t)
|
||||
#define DRM_IOCTL_RADEON_INIT_HEAP DRM_IOW( 0x55, drm_radeon_mem_init_heap_t)
|
||||
|
||||
typedef struct drm_radeon_init {
|
||||
enum {
|
||||
|
|
@ -509,4 +512,25 @@ typedef struct drm_radeon_getparam {
|
|||
int *value;
|
||||
} drm_radeon_getparam_t;
|
||||
|
||||
#define RADEON_MEM_REGION_AGP 1
|
||||
#define RADEON_MEM_REGION_FB 2
|
||||
|
||||
typedef struct drm_radeon_mem_alloc {
|
||||
int region;
|
||||
int alignment;
|
||||
int size;
|
||||
int *region_offset; /* offset from start of fb or agp */
|
||||
} drm_radeon_mem_alloc_t;
|
||||
|
||||
typedef struct drm_radeon_mem_free {
|
||||
int region;
|
||||
int region_offset;
|
||||
} drm_radeon_mem_free_t;
|
||||
|
||||
typedef struct drm_radeon_mem_init_heap {
|
||||
int region;
|
||||
int size;
|
||||
int start;
|
||||
} drm_radeon_mem_init_heap_t;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -61,6 +61,15 @@ typedef struct drm_radeon_depth_clear_t {
|
|||
u32 se_cntl;
|
||||
} drm_radeon_depth_clear_t;
|
||||
|
||||
|
||||
struct mem_block {
|
||||
struct mem_block *next;
|
||||
struct mem_block *prev;
|
||||
int start;
|
||||
int size;
|
||||
int pid; /* 0: free, -1: heap, other: real pids */
|
||||
};
|
||||
|
||||
typedef struct drm_radeon_private {
|
||||
drm_radeon_ring_buffer_t ring;
|
||||
drm_radeon_sarea_t *sarea_priv;
|
||||
|
|
@ -126,6 +135,9 @@ typedef struct drm_radeon_private {
|
|||
drm_map_t *ring_rptr;
|
||||
drm_map_t *buffers;
|
||||
drm_map_t *agp_textures;
|
||||
|
||||
struct mem_block *agp_heap;
|
||||
struct mem_block *fb_heap;
|
||||
} drm_radeon_private_t;
|
||||
|
||||
typedef struct drm_radeon_buf_priv {
|
||||
|
|
@ -164,6 +176,13 @@ extern int radeon_cp_cmdbuf( DRM_IOCTL_ARGS );
|
|||
extern int radeon_cp_getparam( DRM_IOCTL_ARGS );
|
||||
extern int radeon_cp_flip( DRM_IOCTL_ARGS );
|
||||
|
||||
extern int radeon_mem_alloc( DRM_IOCTL_ARGS );
|
||||
extern int radeon_mem_free( DRM_IOCTL_ARGS );
|
||||
extern int radeon_mem_init_heap( DRM_IOCTL_ARGS );
|
||||
extern void radeon_mem_takedown( struct mem_block **heap );
|
||||
extern void radeon_mem_release( struct mem_block *heap );
|
||||
|
||||
|
||||
/* Flags for stats.boxes
|
||||
*/
|
||||
#define RADEON_BOX_DMA_IDLE 0x1
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
/* radeon_state.c -- State support for Radeon -*- linux-c -*-
|
||||
/* radeon_mem.c -- Simple agp/fb memory manager for radeon -*- linux-c -*-
|
||||
*
|
||||
* Copyright 2000 VA Linux Systems, Inc., Fremont, California.
|
||||
* All Rights Reserved.
|
||||
* Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved.
|
||||
*
|
||||
* The Weather Channel (TM) funded Tungsten Graphics to develop the
|
||||
* initial release of the Radeon 8500 driver under the XFree86 license.
|
||||
* This notice must be preserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
|
|
@ -34,44 +37,15 @@
|
|||
|
||||
/* Very simple allocator for agp memory, working on a static range
|
||||
* already mapped into each client's address space.
|
||||
*
|
||||
* TODO: dynamically grow region, make regions truely private to each
|
||||
* client, etc. Make work with FB memory as well as agp.
|
||||
*/
|
||||
struct mem_block {
|
||||
struct mem_block *next;
|
||||
struct mem_block *prev;
|
||||
int start;
|
||||
int size;
|
||||
int pid; /* 0: free, -1: heap, other: real pids */
|
||||
};
|
||||
|
||||
|
||||
static int init(struct mem_block *heap, int start, int size)
|
||||
{
|
||||
struct mem_block *blocks = malloc(sizeof(*blocks));
|
||||
|
||||
if (!blocks)
|
||||
return -ENOMEM;
|
||||
|
||||
blocks->start = start;
|
||||
blocks->size = size;
|
||||
blocks->pid = 0;
|
||||
blocks->next = blocks->prev = heap;
|
||||
|
||||
memset( heap, 0, sizeof(*heap) );
|
||||
heap->pid = -1;
|
||||
heap->next = heap->prev = blocks;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct mem_block *split_block(struct mem_block *p, int start, int size,
|
||||
int pid )
|
||||
{
|
||||
/* Maybe cut off the start of an existing block */
|
||||
if (start > p->start) {
|
||||
struct mem_block *newblock = malloc(sizeof(*newblock));
|
||||
struct mem_block *newblock = kmalloc(sizeof(*newblock),
|
||||
GFP_KERNEL);
|
||||
if (!newblock)
|
||||
goto out;
|
||||
newblock->start = start;
|
||||
|
|
@ -87,7 +61,8 @@ static struct mem_block *split_block(struct mem_block *p, int start, int size,
|
|||
|
||||
/* Maybe cut off the end of an existing block */
|
||||
if (size < p->size) {
|
||||
struct mem_block *newblock = malloc(sizeof(*newblock));
|
||||
struct mem_block *newblock = kmalloc(sizeof(*newblock),
|
||||
GFP_KERNEL);
|
||||
if (!newblock)
|
||||
goto out;
|
||||
newblock->start = start + size;
|
||||
|
|
@ -113,18 +88,27 @@ static struct mem_block *alloc_block( struct mem_block *heap, int size,
|
|||
int mask = (1 << align2)-1;
|
||||
|
||||
for (p = heap->next ; p != heap ; p = p->next) {
|
||||
if (p->pid == 0) {
|
||||
int start = (p->start + mask) & ~mask;
|
||||
if (start + size <= p->start + p->size)
|
||||
return split_block( p, start, size, pid );
|
||||
}
|
||||
int start = (p->start + mask) & ~mask;
|
||||
if (p->pid == 0 && start + size <= p->start + p->size)
|
||||
return split_block( p, start, size, pid );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct mem_block *find_block( struct mem_block *heap, int start )
|
||||
{
|
||||
struct mem_block *p;
|
||||
|
||||
static void free_block( struct mem_block *b )
|
||||
for (p = heap->next ; p != heap ; p = p->next)
|
||||
if (p->start == start)
|
||||
return p;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void free_block( struct mem_block *p )
|
||||
{
|
||||
p->pid = 0;
|
||||
|
||||
|
|
@ -136,7 +120,7 @@ static void free_block( struct mem_block *b )
|
|||
p->size += q->size;
|
||||
p->next = q->next;
|
||||
p->next->prev = p;
|
||||
free(p);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
if (p->prev->pid == 0) {
|
||||
|
|
@ -144,12 +128,44 @@ static void free_block( struct mem_block *b )
|
|||
q->size += p->size;
|
||||
q->next = p->next;
|
||||
q->next->prev = q;
|
||||
free(p);
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_by_pid( struct mem_block *heap, int pid )
|
||||
/* Initialize. How to check for an uninitialized heap?
|
||||
*/
|
||||
static int init_heap(struct mem_block **heap, int start, int size)
|
||||
{
|
||||
struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL);
|
||||
|
||||
if (!blocks)
|
||||
return -ENOMEM;
|
||||
|
||||
*heap = kmalloc(sizeof(**heap), GFP_KERNEL);
|
||||
if (!*heap) {
|
||||
kfree( blocks );
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
blocks->start = start;
|
||||
blocks->size = size;
|
||||
blocks->pid = 0;
|
||||
blocks->next = blocks->prev = *heap;
|
||||
|
||||
memset( *heap, 0, sizeof(**heap) );
|
||||
(*heap)->pid = -1;
|
||||
(*heap)->next = (*heap)->prev = blocks;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Free all blocks associated with the releasing pid.
|
||||
*/
|
||||
void radeon_mem_release( struct mem_block *heap )
|
||||
{
|
||||
int pid = DRM_CURRENTPID;
|
||||
struct mem_block *p;
|
||||
|
||||
for (p = heap->next ; p != heap ; p = p->next) {
|
||||
if (p->pid == pid)
|
||||
p->pid = 0;
|
||||
|
|
@ -164,21 +180,138 @@ static void free_by_pid( struct mem_block *heap, int pid )
|
|||
p->size += q->size;
|
||||
p->next = q->next;
|
||||
p->next->prev = p;
|
||||
free(q);
|
||||
kfree(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void destroy(struct mem_block *heap)
|
||||
/* Shutdown.
|
||||
*/
|
||||
void radeon_mem_takedown( struct mem_block **heap )
|
||||
{
|
||||
struct mem_block *heap;
|
||||
struct mem_block *p;
|
||||
|
||||
for (p = heap->next ; p != heap ; ) {
|
||||
for (p = (*heap)->next ; p != *heap ; ) {
|
||||
struct mem_block *q = p;
|
||||
p = p->next;
|
||||
free(q);
|
||||
kfree(q);
|
||||
}
|
||||
|
||||
heap->next = heap->prev = heap;
|
||||
kfree( *heap );
|
||||
*heap = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* IOCTL HANDLERS */
|
||||
|
||||
static struct mem_block **get_heap( drm_radeon_private_t *dev_priv,
|
||||
int region )
|
||||
{
|
||||
switch( region ) {
|
||||
case RADEON_MEM_REGION_AGP:
|
||||
return &dev_priv->fb_heap;
|
||||
case RADEON_MEM_REGION_FB:
|
||||
return &dev_priv->agp_heap;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int radeon_mem_alloc( DRM_IOCTL_ARGS )
|
||||
{
|
||||
DRM_DEVICE;
|
||||
drm_radeon_private_t *dev_priv = dev->dev_private;
|
||||
drm_radeon_mem_alloc_t alloc;
|
||||
struct mem_block *block, **heap;
|
||||
|
||||
if ( !dev_priv ) {
|
||||
DRM_ERROR( "%s called with no initialization\n", __func__ );
|
||||
return DRM_ERR(EINVAL);
|
||||
}
|
||||
|
||||
DRM_COPY_FROM_USER_IOCTL( alloc, (drm_radeon_mem_alloc_t *)data,
|
||||
sizeof(alloc) );
|
||||
|
||||
heap = get_heap( dev_priv, alloc.region );
|
||||
if (!heap || !*heap)
|
||||
return DRM_ERR(EFAULT);
|
||||
|
||||
/* Make things easier on ourselves: all allocations at least
|
||||
* 4k aligned.
|
||||
*/
|
||||
if (alloc.alignment < 12)
|
||||
alloc.alignment = 12;
|
||||
|
||||
block = alloc_block( *heap, alloc.size, alloc.alignment,
|
||||
DRM_CURRENTPID );
|
||||
|
||||
if ( DRM_COPY_TO_USER( alloc.region_offset, &block->start,
|
||||
sizeof(int) ) ) {
|
||||
DRM_ERROR( "copy_to_user\n" );
|
||||
return DRM_ERR(EFAULT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int radeon_mem_free( DRM_IOCTL_ARGS )
|
||||
{
|
||||
DRM_DEVICE;
|
||||
drm_radeon_private_t *dev_priv = dev->dev_private;
|
||||
drm_radeon_mem_free_t memfree;
|
||||
struct mem_block *block, **heap;
|
||||
|
||||
if ( !dev_priv ) {
|
||||
DRM_ERROR( "%s called with no initialization\n", __func__ );
|
||||
return DRM_ERR(EINVAL);
|
||||
}
|
||||
|
||||
DRM_COPY_FROM_USER_IOCTL( memfree, (drm_radeon_mem_free_t *)data,
|
||||
sizeof(memfree) );
|
||||
|
||||
heap = get_heap( dev_priv, memfree.region );
|
||||
if (!heap || !*heap)
|
||||
return DRM_ERR(EFAULT);
|
||||
|
||||
block = find_block( *heap, memfree.region_offset );
|
||||
if (!block)
|
||||
return DRM_ERR(EFAULT);
|
||||
|
||||
if (block->pid != DRM_CURRENTPID)
|
||||
return DRM_ERR(EPERM);
|
||||
|
||||
free_block( block );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int radeon_mem_init_heap( DRM_IOCTL_ARGS )
|
||||
{
|
||||
DRM_DEVICE;
|
||||
drm_radeon_private_t *dev_priv = dev->dev_private;
|
||||
drm_radeon_mem_init_heap_t initheap;
|
||||
struct mem_block **heap;
|
||||
|
||||
if ( !dev_priv ) {
|
||||
DRM_ERROR( "%s called with no initialization\n", __func__ );
|
||||
return DRM_ERR(EINVAL);
|
||||
}
|
||||
|
||||
DRM_COPY_FROM_USER_IOCTL( initheap, (drm_radeon_mem_init_heap_t *)data,
|
||||
sizeof(initheap) );
|
||||
|
||||
heap = get_heap( dev_priv, initheap.region );
|
||||
if (!heap)
|
||||
return DRM_ERR(EFAULT);
|
||||
|
||||
if (*heap) {
|
||||
DRM_ERROR("heap already initialized?");
|
||||
return DRM_ERR(EFAULT);
|
||||
}
|
||||
|
||||
return init_heap( heap, initheap.start, initheap.size );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1306,6 +1306,9 @@ static int radeon_do_init_pageflip( drm_device_t *dev )
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Called whenever a client dies, from DRM(release).
|
||||
* NOTE: Lock isn't necessarily held when this is called!
|
||||
*/
|
||||
int radeon_do_cleanup_pageflip( drm_device_t *dev )
|
||||
{
|
||||
drm_radeon_private_t *dev_priv = dev->dev_private;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue