summaryrefslogtreecommitdiff
path: root/dove_bufmgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'dove_bufmgr.c')
-rw-r--r--dove_bufmgr.c368
1 files changed, 310 insertions, 58 deletions
diff --git a/dove_bufmgr.c b/dove_bufmgr.c
index 32e192a..eb2596b 100644
--- a/dove_bufmgr.c
+++ b/dove_bufmgr.c
@@ -4,9 +4,11 @@
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
+#include <time.h>
#include <drm.h>
+#include "libdrm_lists.h"
#include "dove_bufmgr.h"
#include "dove_ioctl.h"
@@ -16,10 +18,58 @@
(type *)( (char *)__mptr - offsetof(type,member) );})
#endif
+/* The interval in seconds between cache cleans */
+#define BO_CACHE_CLEAN_INTERVAL 1
+/* The maximum age in seconds of a BO in the cache */
+#define BO_CACHE_MAX_AGE 2
+/* Number of buckets in the BO cache */
+#define NUM_BUCKETS (3*9)
+
+/*
+ * These sizes come from the i915 DRM backend - which uses roughly
+ * for n = 2..
+ * (4096 << n) + (4096 << n) * 1 / 4
+ * (4096 << n) + (4096 << n) * 2 / 4
+ * (4096 << n) + (4096 << n) * 3 / 4
+ * The reasoning being that powers of two are too wasteful in X.
+ */
+static size_t bucket_size[NUM_BUCKETS] = {
+ 4096, 8192, 12288,
+ 20480, 24576, 28672,
+ 40960, 49152, 57344,
+ 81920, 98304, 114688,
+ 163840, 196608, 229376,
+ 327680, 393216, 458752,
+ 655360, 786432, 917504,
+ 1310720, 1572864, 1835008,
+ 2621440, 3145728, 3670016,
+};
+
+struct dove_bucket {
+ drmMMListHead head; /* LRU list of bos in this size */
+ size_t size;
+};
+
+struct dove_bo_cache {
+ struct dove_bucket buckets[NUM_BUCKETS];
+ drmMMListHead head; /* LRU list of all freed bos */
+ time_t last_cleaned;
+};
+
+struct drm_dove_bufmgr {
+ struct dove_bo_cache cache;
+ int fd;
+};
+
struct dove_bo {
struct drm_dove_bo bo;
- uint32_t ref;
- uint32_t name; /* Global name */
+ struct drm_dove_bufmgr *mgr; /* manager associated with this bo */
+ drmMMListHead bucket; /* Cache bucket list */
+ drmMMListHead free; /* Free list */
+ time_t free_time; /* Time this bo was freed */
+ size_t alloc_size; /* Allocated size */
+ uint32_t ref; /* Reference count */
+ uint32_t name; /* Global name */
};
#define to_dove_bo(_bo) container_of(_bo, struct dove_bo, bo)
@@ -55,9 +105,164 @@ struct drm_mode_map_dumb {
#define DRM_IOCTL_MODE_MAP_DUMB DRM_IOWR(0xB3, struct drm_mode_map_dumb)
#endif
-struct drm_dove_bo *drm_dove_bo_create_phys(int fd, uint32_t phys, size_t size)
+/* Given a width and bpp, return the pitch of a bo */
+static unsigned dove_bo_pitch(unsigned width, unsigned bpp)
+{
+ unsigned pitch = bpp != 4 ? width * ((bpp + 7) / 8) : width / 2;
+
+ /* 88AP510 spec recommends pitch be a multiple of 128 */
+ return (pitch + 127) & ~127;
+}
+
+/* Given the pitch and height, return the allocated size in bytes of a bo */
+static size_t dove_bo_size(unsigned pitch, unsigned height)
+{
+ return pitch * height;
+}
+
+static size_t dove_bo_round_size(size_t size)
+{
+ if (size > 1048576)
+ size = (size + 1048575) & ~1048575;
+ else if (size > 65536)
+ size = (size + 65535) & ~65535;
+ else
+ size = (size + 4095) & ~4095;
+ return size;
+}
+
+static void dove_bo_free(struct dove_bo *bo)
+{
+ int ret, fd = bo->mgr->fd;
+
+ if (bo->bo.ptr) {
+ munmap(bo->bo.ptr, bo->alloc_size);
+ bo->bo.ptr = NULL;
+ }
+
+ if (bo->bo.type == DRM_DOVE_BO_DUMB) {
+ struct drm_mode_destroy_dumb arg;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.handle = bo->bo.handle;
+ ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg);
+ } else {
+ struct drm_gem_close close;
+
+ memset(&close, 0, sizeof(close));
+ close.handle = bo->bo.handle;
+ ret = ioctl(fd, DRM_IOCTL_GEM_CLOSE, &close);
+ }
+
+ if (ret == 0)
+ free(bo);
+}
+
+static void dove_bo_cache_init(struct dove_bo_cache *cache)
+{
+ struct timespec time;
+ unsigned i;
+
+ clock_gettime(CLOCK_MONOTONIC, &time);
+
+ cache->last_cleaned = time.tv_sec;
+ DRMINITLISTHEAD(&cache->head);
+
+ for (i = 0; i < NUM_BUCKETS; i++) {
+ DRMINITLISTHEAD(&cache->buckets[i].head);
+ cache->buckets[i].size = bucket_size[i];
+ }
+}
+
+static void dove_bo_cache_fini(struct dove_bo_cache *cache)
+{
+ while (!DRMLISTEMPTY(&cache->head)) {
+ struct dove_bo *bo;
+
+ bo = DRMLISTENTRY(struct dove_bo, cache->head.next, free);
+
+ DRMLISTDEL(&bo->bucket);
+ DRMLISTDEL(&bo->free);
+
+ dove_bo_free(bo);
+ }
+}
+
+static struct dove_bucket *dove_find_bucket(struct dove_bo_cache *cache, size_t size)
+{
+ unsigned i;
+
+ for (i = 0; i < NUM_BUCKETS; i++) {
+ struct dove_bucket *bucket = &cache->buckets[i];
+
+ if (bucket->size >= size)
+ return bucket;
+ }
+
+ return NULL;
+}
+
+static void dove_bo_cache_clean(struct dove_bo_cache *cache, time_t time)
+{
+ if (time - cache->last_cleaned < BO_CACHE_CLEAN_INTERVAL)
+ return;
+
+ cache->last_cleaned = time;
+
+ while (!DRMLISTEMPTY(&cache->head)) {
+ struct dove_bo *bo;
+
+ bo = DRMLISTENTRY(struct dove_bo, cache->head.next, free);
+ if (time - bo->free_time < BO_CACHE_MAX_AGE)
+ break;
+
+ DRMLISTDEL(&bo->bucket);
+ DRMLISTDEL(&bo->free);
+
+ dove_bo_free(bo);
+ }
+}
+
+static struct dove_bo *dove_bo_bucket_get(struct dove_bucket *bucket, size_t size)
+{
+ struct dove_bo *bo = NULL;
+
+ if (!DRMLISTEMPTY(&bucket->head)) {
+ drmMMListHead *entry = bucket->head.next;
+
+ bo = DRMLISTENTRY(struct dove_bo, entry, bucket);
+ DRMLISTDEL(&bo->bucket);
+ DRMLISTDEL(&bo->free);
+ }
+ return bo;
+}
+
+static void dove_bo_cache_put(struct dove_bo *bo)
+{
+ struct dove_bo_cache *cache = &bo->mgr->cache;
+ struct dove_bucket *bucket = dove_find_bucket(cache, bo->alloc_size);
+
+ if (bucket) {
+ struct timespec time;
+
+ clock_gettime(CLOCK_MONOTONIC, &time);
+
+ bo->free_time = time.tv_sec;
+ DRMLISTADDTAIL(&bo->bucket, &bucket->head);
+ DRMLISTADDTAIL(&bo->free, &cache->head);
+
+ dove_bo_cache_clean(cache, time.tv_sec);
+
+ return;
+ }
+ dove_bo_free(bo);
+}
+
+struct drm_dove_bo *drm_dove_bo_create_phys(struct drm_dove_bufmgr *mgr,
+ uint32_t phys, size_t size)
{
struct dove_bo *bo;
+ int fd = mgr->fd;
bo = calloc(1, sizeof *bo);
if (bo) {
@@ -78,44 +283,80 @@ struct drm_dove_bo *drm_dove_bo_create_phys(int fd, uint32_t phys, size_t size)
bo->bo.size = size;
bo->bo.phys = phys;
bo->bo.type = DRM_DOVE_BO_LINEAR;
+ bo->alloc_size = size;
bo->ref = 1;
+ bo->mgr = mgr;
}
return &bo->bo;
}
-struct drm_dove_bo *drm_dove_bo_create(int fd, unsigned w, unsigned h, unsigned bpp)
+struct drm_dove_bo *drm_dove_bo_create(struct drm_dove_bufmgr *mgr,
+ unsigned w, unsigned h, unsigned bpp)
{
+ struct drm_dove_gem_create arg;
+ struct dove_bucket *bucket;
struct dove_bo *bo;
+ unsigned pitch;
+ size_t alloc_size;
+ int fd = mgr->fd;
+ int ret;
- bo = calloc(1, sizeof *bo);
- if (bo) {
- struct drm_dove_gem_create arg;
- int ret;
+ pitch = dove_bo_pitch(w, bpp);
+ alloc_size = dove_bo_size(pitch, h);
+
+ /* Try to find a bucket for this allocation */
+ bucket = dove_find_bucket(&mgr->cache, alloc_size);
+ if (bucket) {
+ /* Can we allocate from our cache? */
+ bo = dove_bo_bucket_get(bucket, alloc_size);
+ if (bo) {
+ bo->bo.size = pitch * h;
+ bo->bo.pitch = pitch;
+ bo->ref = 1;
+ return &bo->bo;
+ }
- memset(&arg, 0, sizeof(arg));
- arg.width = w;
- arg.height = h;
- arg.bpp = bpp;
+ /* Otherwise, allocate a bo of the bucket size */
+ alloc_size = bucket->size;
+ } else {
+ /* No bucket, so round the size up according to our old rules */
+ alloc_size = dove_bo_round_size(alloc_size);
+ }
- ret = drmIoctl(fd, DRM_IOCTL_DOVE_GEM_CREATE, &arg);
- if (ret) {
- free(bo);
- return NULL;
- }
+ /* No, create a new bo */
+ bo = calloc(1, sizeof *bo);
+ if (!bo)
+ return NULL;
- bo->bo.ref = 1;
- bo->bo.handle = arg.handle;
- bo->bo.size = arg.size;
- bo->bo.pitch = arg.pitch;
- bo->bo.type = DRM_DOVE_BO_SHMEM;
- bo->ref = 1;
+ memset(&arg, 0, sizeof(arg));
+ arg.width = w;
+ arg.height = h;
+ arg.bpp = bpp;
+ arg.size = alloc_size;
+
+ ret = drmIoctl(fd, DRM_IOCTL_DOVE_GEM_CREATE, &arg);
+ if (ret) {
+ free(bo);
+ return NULL;
}
+
+ bo->bo.ref = 1;
+ bo->bo.handle = arg.handle;
+ bo->bo.size = pitch * h;
+ bo->bo.pitch = pitch;
+ bo->bo.type = DRM_DOVE_BO_SHMEM;
+ bo->alloc_size = alloc_size;
+ bo->ref = 1;
+ bo->mgr = mgr;
+
return &bo->bo;
}
-struct drm_dove_bo *drm_dove_bo_create_from_name(int fd, uint32_t name)
+struct drm_dove_bo *drm_dove_bo_create_from_name(struct drm_dove_bufmgr *mgr,
+ uint32_t name)
{
struct dove_bo *bo;
+ int fd = mgr->fd;
bo = calloc(1, sizeof *bo);
if (bo) {
@@ -133,16 +374,19 @@ struct drm_dove_bo *drm_dove_bo_create_from_name(int fd, uint32_t name)
bo->bo.handle = arg.handle;
bo->bo.size = arg.size;
bo->bo.type = DRM_DOVE_BO_LINEAR; /* assumed */
+ bo->alloc_size = arg.size;
bo->ref = 1;
bo->name = name;
+ bo->mgr = mgr;
}
return &bo->bo;
}
-struct drm_dove_bo *drm_dove_bo_dumb_create(int fd, unsigned w, unsigned h,
- unsigned bpp)
+struct drm_dove_bo *drm_dove_bo_dumb_create(struct drm_dove_bufmgr *mgr,
+ unsigned w, unsigned h, unsigned bpp)
{
struct dove_bo *bo;
+ int fd = mgr->fd;
bo = calloc(1, sizeof *bo);
if (bo) {
@@ -164,51 +408,37 @@ struct drm_dove_bo *drm_dove_bo_dumb_create(int fd, unsigned w, unsigned h,
bo->bo.size = arg.size;
bo->bo.pitch = arg.pitch;
bo->bo.type = DRM_DOVE_BO_DUMB;
+ bo->alloc_size = arg.size;
bo->ref = 1;
+ bo->mgr = mgr;
}
return &bo->bo;
}
-void drm_dove_bo_get(int fd, struct drm_dove_bo *dbo)
+void drm_dove_bo_get(struct drm_dove_bo *dbo)
{
struct dove_bo *bo = to_dove_bo(dbo);
bo->ref++;
}
-void drm_dove_bo_put(int fd, struct drm_dove_bo *dbo)
+void drm_dove_bo_put(struct drm_dove_bo *dbo)
{
struct dove_bo *bo = to_dove_bo(dbo);
if (bo->ref-- == 1) {
int ret;
- if (bo->bo.ptr) {
- munmap(bo->bo.ptr, bo->bo.size);
- bo->bo.ptr = NULL;
- }
-
- if (bo->bo.type == DRM_DOVE_BO_DUMB) {
- struct drm_mode_destroy_dumb arg;
-
- memset(&arg, 0, sizeof(arg));
- arg.handle = bo->bo.handle;
- ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg);
- } else {
- struct drm_gem_close close;
-
- memset(&close, 0, sizeof(close));
- close.handle = bo->bo.handle;
- ret = ioctl(fd, DRM_IOCTL_GEM_CLOSE, &close);
- }
-
- if (ret == 0)
- free(bo);
+ if (bo->bo.type == DRM_DOVE_BO_SHMEM)
+ dove_bo_cache_put(bo);
+ else
+ dove_bo_free(bo);
}
}
-int drm_dove_bo_flink(int fd, struct drm_dove_bo *dbo, uint32_t *name)
+int drm_dove_bo_flink(struct drm_dove_bo *dbo, uint32_t *name)
{
struct dove_bo *bo = to_dove_bo(dbo);
+ int fd = bo->mgr->fd;
if (!bo->name) {
struct drm_gem_flink flink;
@@ -225,11 +455,11 @@ int drm_dove_bo_flink(int fd, struct drm_dove_bo *dbo, uint32_t *name)
return 0;
}
-int drm_dove_bo_map(int fd, struct drm_dove_bo *dbo)
+int drm_dove_bo_map(struct drm_dove_bo *dbo)
{
struct dove_bo *bo = to_dove_bo(dbo);
void *map;
- int ret;
+ int ret, fd = bo->mgr->fd;
if (bo->bo.ptr)
return 0;
@@ -244,7 +474,7 @@ int drm_dove_bo_map(int fd, struct drm_dove_bo *dbo)
if (ret)
return ret;
- map = mmap(0, bo->bo.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ map = mmap(0, bo->alloc_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
arg.offset);
if (map == MAP_FAILED)
@@ -255,7 +485,7 @@ int drm_dove_bo_map(int fd, struct drm_dove_bo *dbo)
memset(&arg, 0, sizeof(arg));
arg.handle = bo->bo.handle;
arg.offset = 0;
- arg.size = bo->bo.size;
+ arg.size = bo->alloc_size;
ret = drmIoctl(fd, DRM_IOCTL_DOVE_GEM_MMAP, &arg);
if (ret)
@@ -272,11 +502,11 @@ int drm_dove_bo_map(int fd, struct drm_dove_bo *dbo)
return 0;
}
-uint32_t drm_dove_bo_phys(int fd, struct drm_dove_bo *dbo)
+uint32_t drm_dove_bo_phys(struct drm_dove_bo *dbo)
{
struct dove_bo *bo = to_dove_bo(dbo);
struct drm_dove_gem_prop arg;
- int ret;
+ int ret, fd = bo->mgr->fd;
memset(&arg, 0, sizeof(arg));
arg.handle = bo->bo.handle;
@@ -286,13 +516,15 @@ uint32_t drm_dove_bo_phys(int fd, struct drm_dove_bo *dbo)
return ret ? -1 : (uint32_t)arg.phys;
}
-int drm_dove_bo_subdata(int fd, struct drm_dove_bo *bo, unsigned long offset,
+int drm_dove_bo_subdata(struct drm_dove_bo *dbo, unsigned long offset,
unsigned long size, const void *data)
{
+ struct dove_bo *bo = to_dove_bo(dbo);
struct drm_dove_gem_pwrite arg;
+ int fd = bo->mgr->fd;
memset(&arg, 0, sizeof(arg));
- arg.handle = bo->handle;
+ arg.handle = bo->bo.handle;
arg.offset = offset;
arg.size = size;
arg.ptr = (uint64_t)(uintptr_t)data;
@@ -300,3 +532,23 @@ int drm_dove_bo_subdata(int fd, struct drm_dove_bo *bo, unsigned long offset,
return drmIoctl(fd, DRM_IOCTL_DOVE_GEM_PWRITE, &arg);
}
+int drm_dove_init(int fd, struct drm_dove_bufmgr **mgrp)
+{
+ struct drm_dove_bufmgr *mgr;
+
+ mgr = calloc(1, sizeof(*mgr));
+ if (!mgr)
+ return -1;
+
+ dove_bo_cache_init(&mgr->cache);
+ mgr->fd = fd;
+ *mgrp = mgr;
+
+ return 0;
+}
+
+void drm_dove_fini(struct drm_dove_bufmgr *mgr)
+{
+ dove_bo_cache_fini(&mgr->cache);
+ free(mgr);
+}