drm: Branch 'master' - 20 commits

[email protected] (Rob Clark)
Newsgroups gmane.comp.video.dri.patches
Message-ID <[email protected]>
 freedreno/Makefile.sources       |    1 
 freedreno/README                 |   16 -
 freedreno/freedreno_bo.c         |  141 +------------
 freedreno/freedreno_bo_cache.c   |  222 +++++++++++++++++++++
 freedreno/freedreno_device.c     |   62 +----
 freedreno/freedreno_drmif.h      |   14 +
 freedreno/freedreno_priv.h       |   38 +++
 freedreno/freedreno_ringbuffer.c |   51 ++++
 freedreno/freedreno_ringbuffer.h |   16 -
 freedreno/kgsl/README            |   26 ++
 freedreno/kgsl/kgsl_bo.c         |    6 
 freedreno/kgsl/kgsl_ringbuffer.c |   13 -
 freedreno/msm/msm_bo.c           |   20 +
 freedreno/msm/msm_device.c       |    3 
 freedreno/msm/msm_drm.h          |  109 ++++++----
 freedreno/msm/msm_priv.h         |   17 -
 freedreno/msm/msm_ringbuffer.c   |  410 ++++++++++++++++++++++++++++-----------
 util_double_list.h               |    6 
 18 files changed, 796 insertions(+), 375 deletions(-)

New commits:
commit b59ed1881890de75fd13eb7056396bc7848760bc
Author: Rob Clark <[email protected]>
Date:   Wed Jul 20 13:11:58 2016 -0400

    freedreno: move legacy kgsl related README
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/README b/freedreno/README
deleted file mode 100644
index ae22e01..0000000
--- a/freedreno/README
+++ /dev/null
@@ -1,16 +0,0 @@
-Note that current msm kernel driver is a bit strange.  It provides a
-DRM interface for GEM, which is basically sufficient to have DRI2
-working.  But it does not provide KMS.  And interface to 2d and 3d
-cores is via different other devices (/dev/kgsl-*).  This is not
-quite how I'd write a DRM driver, but at this stage it is useful for
-xf86-video-freedreno and fdre (and eventual gallium driver) to be
-able to work on existing kernel driver from QCOM, to allow to
-capture cmdstream dumps from the binary blob drivers without having
-to reboot.  So libdrm_freedreno attempts to hide most of the crazy.
-The intention is that when there is a proper kernel driver, it will
-be mostly just changes in libdrm_freedreno to adapt the gallium
-driver and xf86-video-freedreno (ignoring the fbdev->KMS changes).
-
-So don't look at freedreno as an example of how to write a libdrm
-module or a DRM driver.. it is just an attempt to paper over a non-
-standard kernel driver architecture.
diff --git a/freedreno/kgsl/README b/freedreno/kgsl/README
new file mode 100644
index 0000000..56874b4
--- /dev/null
+++ b/freedreno/kgsl/README
@@ -0,0 +1,26 @@
+This is a historical discription of what is now the kgsl backend
+in libdrm freedreno (before the upstream drm/msm driver).  Note
+that the kgsl backend requires the "kgsl-drm" shim driver, which
+usually is in disrepair (QCOM does not build it for android), and
+due to random differences between different downstream android
+kernel branches it may or may not work.  So YMMV.
+
+Original README:
+----------------
+
+Note that current msm kernel driver is a bit strange.  It provides a
+DRM interface for GEM, which is basically sufficient to have DRI2
+working.  But it does not provide KMS.  And interface to 2d and 3d
+cores is via different other devices (/dev/kgsl-*).  This is not
+quite how I'd write a DRM driver, but at this stage it is useful for
+xf86-video-freedreno and fdre (and eventual gallium driver) to be
+able to work on existing kernel driver from QCOM, to allow to
+capture cmdstream dumps from the binary blob drivers without having
+to reboot.  So libdrm_freedreno attempts to hide most of the crazy.
+The intention is that when there is a proper kernel driver, it will
+be mostly just changes in libdrm_freedreno to adapt the gallium
+driver and xf86-video-freedreno (ignoring the fbdev->KMS changes).
+
+So don't look at freedreno as an example of how to write a libdrm
+module or a DRM driver.. it is just an attempt to paper over a non-
+standard kernel driver architecture.
commit 6a23bd4b3c52fcd7529062b31c36dd03ae0cdd75
Author: Rob Clark <[email protected]>
Date:   Tue Jun 28 13:33:07 2016 -0400

    freedreno/msm: use hashtable to track bo idx
    
    Note: cache the last ring the bo was emitted on, to avoid excess
    hashtable lookups.  We do this by tracking ring seqno to avoid
    problems with dangling pointers.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/msm/msm_priv.h b/freedreno/msm/msm_priv.h
index 1f44398..6d670aa 100644
--- a/freedreno/msm/msm_priv.h
+++ b/freedreno/msm/msm_priv.h
@@ -40,6 +40,7 @@
 struct msm_device {
 	struct fd_device base;
 	struct fd_bo_cache ring_cache;
+	unsigned ring_cnt;
 };
 
 static inline struct msm_device * to_msm_device(struct fd_device *x)
@@ -72,18 +73,11 @@ struct msm_bo {
 	struct fd_bo base;
 	uint64_t offset;
 	uint64_t presumed;
-	/* in the common case, a bo won't be referenced by more than a single
-	 * (parent) ring[*].  So to avoid looping over all the bo's in the
-	 * reloc table to find the idx of a bo that might already be in the
-	 * table, we cache the idx in the bo.  But in order to detect the
-	 * slow-path where bo is ref'd in multiple rb's, we also must track
-	 * the current_ring for which the idx is valid.  See bo2idx().
-	 *
-	 * [*] in case multiple ringbuffers, ie. one toplevel and other rb(s)
-	 *     used for IB target(s), the toplevel rb is the parent which is
-	 *     tracking bo's for the submit
+	/* to avoid excess hashtable lookups, cache the ring this bo was
+	 * last emitted on (since that will probably also be the next ring
+	 * it is emitted on)
 	 */
-	struct fd_ringbuffer *current_ring;
+	unsigned current_ring_seqno;
 	uint32_t idx;
 };
 
diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c
index 86fc83e..fbfaefa 100644
--- a/freedreno/msm/msm_ringbuffer.c
+++ b/freedreno/msm/msm_ringbuffer.c
@@ -92,6 +92,11 @@ struct msm_ringbuffer {
 
 	int is_growable;
 	unsigned cmd_count;
+
+	unsigned seqno;
+
+	/* maps fd_bo to idx: */
+	void *bo_table;
 };
 
 static inline struct msm_ringbuffer * to_msm_ringbuffer(struct fd_ringbuffer *x)
@@ -217,21 +222,24 @@ static uint32_t bo2idx(struct fd_ringbuffer *ring, struct fd_bo *bo, uint32_t fl
 	struct msm_bo *msm_bo = to_msm_bo(bo);
 	uint32_t idx;
 	pthread_mutex_lock(&idx_lock);
-	if (!msm_bo->current_ring) {
-		idx = append_bo(ring, bo);
-		msm_bo->current_ring = ring;
-		msm_bo->idx = idx;
-	} else if (msm_bo->current_ring == ring) {
+	if (msm_bo->current_ring_seqno == msm_ring->seqno) {
 		idx = msm_bo->idx;
 	} else {
-		/* slow-path: */
-		for (idx = 0; idx < msm_ring->nr_bos; idx++)
-			if (msm_ring->bos[idx] == bo)
-				break;
-		if (idx == msm_ring->nr_bos) {
-			/* not found */
+		void *val;
+
+		if (!msm_ring->bo_table)
+			msm_ring->bo_table = drmHashCreate();
+
+		if (!drmHashLookup(msm_ring->bo_table, bo->handle, &val)) {
+			/* found */
+			idx = (uint32_t)val;
+		} else {
 			idx = append_bo(ring, bo);
+			val = (void *)idx;
+			drmHashInsert(msm_ring->bo_table, bo->handle, val);
 		}
+		msm_bo->current_ring_seqno = msm_ring->seqno;
+		msm_bo->idx = idx;
 	}
 	pthread_mutex_unlock(&idx_lock);
 	if (flags & FD_RELOC_READ)
@@ -318,7 +326,7 @@ static void flush_reset(struct fd_ringbuffer *ring)
 
 	for (i = 0; i < msm_ring->nr_bos; i++) {
 		struct msm_bo *msm_bo = to_msm_bo(msm_ring->bos[i]);
-		msm_bo->current_ring = NULL;
+		msm_bo->current_ring_seqno = 0;
 		fd_bo_del(&msm_bo->base);
 	}
 
@@ -333,6 +341,11 @@ static void flush_reset(struct fd_ringbuffer *ring)
 	msm_ring->nr_cmds = 0;
 	msm_ring->nr_bos = 0;
 
+	if (msm_ring->bo_table) {
+		drmHashDestroy(msm_ring->bo_table);
+		msm_ring->bo_table = NULL;
+	}
+
 	if (msm_ring->is_growable) {
 		delete_cmds(msm_ring);
 	} else {
@@ -551,6 +564,7 @@ drm_private struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe,
 	}
 
 	list_inithead(&msm_ring->cmd_list);
+	msm_ring->seqno = ++to_msm_device(pipe->dev)->ring_cnt;
 
 	ring = &msm_ring->base;
 	ring->funcs = &funcs;
commit 419a154dbef839b920689bea72aa9af41b2b114f
Author: Rob Clark <[email protected]>
Date:   Mon Jun 20 14:06:24 2016 -0400

    freedreno: support growable cmdstream buffers
    
    The issue that userspace needed to solve is that there is ~two orders of
    magnitude size difference in cmdstream buffers (both for gmem commands
    and for draw commands), and that the previous practice of allocating
    worst-case sizes is quite wasteful.  Previously a submit would be
    constructed (for example) like:
    
      CMD  TARGET  DESCRIPTION
       g0    N     gmem/tiling commands
       b0    Y     binning commands
       d0    Y     draw commands
    
    Which, after the one non-IB-target cmd buffer is inserted into the
    kernel controlled ringbuffer, looks like (not to scale):
    
             b0:           d0:
            +-----+       +-----+
       IB1  | ... |       | ... |
            +-----+       +-----+
             ^             ^
             |             |
             +-----+       +-+---------+
             g0:   |         |         |
            +----+----+----+----+----+----+----
       IB0  | .. | IB | .. | IB | .. | IB | ...
            +----+----+----+----+----+----+----
             ^              tile0     tile1
             |
             +-----------+
      userspace          |
      ~~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      kernel             |
                   ----+----+----
       ringbuffer  ... | IB | ...
                   ----+----+----
    
    Now, multiple physical cmdstream buffers per fd_ringbuffer are supported,
    so this becomes:
    
      CMD  TARGET  DESCRIPTION
       g0    N
       ...   N     gmem/tiling commands
       gN    N
       b0    Y
       ...   Y     binning commands
       bN    Y
       d0    Y
       ...   Y     draw commands
       dN    Y
    
    Which, after the non-IB-target cmd buffers (g0..gN) are inserted into
    the kernel controlled ringbuffer, looks like:
    
                 b0:      b1            d0:      d1
                +-----+  +-----+        +-----+  +-----+
           IB1  | ... |  | ... | ...    | ... |  | ... | ...
                +-----+  +-----+        +-----+  +-----+
                 ^        ^              ^        ^
                 |        |              |        |
                 |        +-+            |  +-----+------+
                 +-----+    |            |  |            |
                       |    |         +--+----------+    |
                 g0:   |    |         |     |       |    |
                +----+----+----+----+----+----+---+----+----+----
           IB0  | .. | IB | IB | .. | IB | IB |.. | IB | IB |...
                +----+----+----+----+----+----+---+----+----+----
                 ^                   tile0         tile1
                 | to b0  to b1
                 |   |      |          to|d0    to|d1
                 |   |      +----+       |      +-+-----------+
                 |   |           |       |      |             |
                 |   +------+    |       +-+-------------+    |
                 |    g1:   |    |         |    |        |    |
                 |   +----+----+----+----+----+----+---+----+----+----
           IB0   |   | .. | IB | IB | .. | IB | IB |.. | IB | IB |...
                 |   +----+----+----+----+----+----+---+----+----+----
                 |    ^                   tileX         tileY
                 |    |
                 |    +-----------+
                 +-----------+    |
          userspace          |    |
          ~~~~~~~~~~~~~~~~~~~|~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          kernel             |    |
                       ----+----+----+----
           ringbuffer  ... | IB | IB | ...
                       ----+----+----+----
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h
index 9737b32..cdfdbe8 100644
--- a/freedreno/freedreno_priv.h
+++ b/freedreno/freedreno_priv.h
@@ -134,12 +134,14 @@ struct fd_ringmarker {
 struct fd_ringbuffer_funcs {
 	void * (*hostptr)(struct fd_ringbuffer *ring);
 	int (*flush)(struct fd_ringbuffer *ring, uint32_t *last_start);
+	void (*grow)(struct fd_ringbuffer *ring, uint32_t size);
 	void (*reset)(struct fd_ringbuffer *ring);
 	void (*emit_reloc)(struct fd_ringbuffer *ring,
 			const struct fd_reloc *reloc);
-	void (*emit_reloc_ring)(struct fd_ringbuffer *ring,
-			struct fd_ringbuffer *target,
+	uint32_t (*emit_reloc_ring)(struct fd_ringbuffer *ring,
+			struct fd_ringbuffer *target, uint32_t cmd_idx,
 			uint32_t submit_offset, uint32_t size);
+	uint32_t (*cmd_count)(struct fd_ringbuffer *ring);
 	void (*destroy)(struct fd_ringbuffer *ring);
 };
 
diff --git a/freedreno/freedreno_ringbuffer.c b/freedreno/freedreno_ringbuffer.c
index 34a06d8..22dafb3 100644
--- a/freedreno/freedreno_ringbuffer.c
+++ b/freedreno/freedreno_ringbuffer.c
@@ -45,10 +45,9 @@ fd_ringbuffer_new(struct fd_pipe *pipe, uint32_t size)
 	if (!ring)
 		return NULL;
 
-	ring->size = size;
 	ring->pipe = pipe;
 	ring->start = ring->funcs->hostptr(ring);
-	ring->end = &(ring->start[size/4]);
+	ring->end = &(ring->start[ring->size/4]);
 
 	ring->cur = ring->last_start = ring->start;
 
@@ -87,6 +86,22 @@ int fd_ringbuffer_flush(struct fd_ringbuffer *ring)
 	return ring->funcs->flush(ring, ring->last_start);
 }
 
+void fd_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t ndwords)
+{
+	assert(ring->funcs->grow);     /* unsupported on kgsl */
+
+	/* there is an upper bound on IB size, which appears to be 0x100000 */
+	if (ring->size < 0x100000)
+		ring->size *= 2;
+
+	ring->funcs->grow(ring, ring->size);
+
+	ring->start = ring->funcs->hostptr(ring);
+	ring->end = &(ring->start[ring->size/4]);
+
+	ring->cur = ring->last_start = ring->start;
+}
+
 uint32_t fd_ringbuffer_timestamp(struct fd_ringbuffer *ring)
 {
 	return ring->last_timestamp;
@@ -108,7 +123,14 @@ void fd_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
 	submit_offset = offset_bytes(target->cur, target->ring->start);
 	size = offset_bytes(end->cur, target->cur);
 
-	ring->funcs->emit_reloc_ring(ring, target->ring, submit_offset, size);
+	ring->funcs->emit_reloc_ring(ring, target->ring, 0, submit_offset, size);
+}
+
+uint32_t fd_ringbuffer_cmd_count(struct fd_ringbuffer *ring)
+{
+	if (!ring->funcs->cmd_count)
+		return 1;
+	return ring->funcs->cmd_count(ring);
 }
 
 uint32_t
@@ -116,9 +138,7 @@ fd_ringbuffer_emit_reloc_ring_full(struct fd_ringbuffer *ring,
 		struct fd_ringbuffer *target, uint32_t cmd_idx)
 {
 	uint32_t size = offset_bytes(target->cur, target->start);
-	assert(cmd_idx == 0);
-	ring->funcs->emit_reloc_ring(ring, target, 0, size);
-	return size;
+	return ring->funcs->emit_reloc_ring(ring, target, cmd_idx, 0, size);
 }
 
 struct fd_ringmarker * fd_ringmarker_new(struct fd_ringbuffer *ring)
diff --git a/freedreno/freedreno_ringbuffer.h b/freedreno/freedreno_ringbuffer.h
index 643f50b..8899b5d 100644
--- a/freedreno/freedreno_ringbuffer.h
+++ b/freedreno/freedreno_ringbuffer.h
@@ -56,6 +56,7 @@ void fd_ringbuffer_set_parent(struct fd_ringbuffer *ring,
 		struct fd_ringbuffer *parent);
 void fd_ringbuffer_reset(struct fd_ringbuffer *ring);
 int fd_ringbuffer_flush(struct fd_ringbuffer *ring);
+void fd_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t ndwords);
 uint32_t fd_ringbuffer_timestamp(struct fd_ringbuffer *ring);
 
 static inline void fd_ringbuffer_emit(struct fd_ringbuffer *ring,
@@ -77,6 +78,7 @@ struct fd_reloc {
 void fd_ringbuffer_reloc(struct fd_ringbuffer *ring, const struct fd_reloc *reloc);
 will_be_deprecated void fd_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
 		struct fd_ringmarker *target, struct fd_ringmarker *end);
+uint32_t fd_ringbuffer_cmd_count(struct fd_ringbuffer *ring);
 uint32_t fd_ringbuffer_emit_reloc_ring_full(struct fd_ringbuffer *ring,
 		struct fd_ringbuffer *target, uint32_t cmd_idx);
 
diff --git a/freedreno/kgsl/kgsl_ringbuffer.c b/freedreno/kgsl/kgsl_ringbuffer.c
index a0bc9d0..7b3298a 100644
--- a/freedreno/kgsl/kgsl_ringbuffer.c
+++ b/freedreno/kgsl/kgsl_ringbuffer.c
@@ -173,12 +173,14 @@ static void kgsl_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
 	kgsl_pipe_add_submit(to_kgsl_pipe(ring->pipe), kgsl_bo);
 }
 
-static void kgsl_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
-		struct fd_ringbuffer *target,
+static uint32_t kgsl_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
+		struct fd_ringbuffer *target, uint32_t cmd_idx,
 		uint32_t submit_offset, uint32_t size)
 {
 	struct kgsl_ringbuffer *target_ring = to_kgsl_ringbuffer(target);
+	assert(cmd_idx == 0);
 	(*ring->cur++) = target_ring->bo->gpuaddr + submit_offset;
+	return size;
 }
 
 static void kgsl_ringbuffer_destroy(struct fd_ringbuffer *ring)
@@ -213,6 +215,7 @@ drm_private struct fd_ringbuffer * kgsl_ringbuffer_new(struct fd_pipe *pipe,
 
 	ring = &kgsl_ring->base;
 	ring->funcs = &funcs;
+	ring->size = size;
 
 	kgsl_ring->bo = kgsl_rb_bo_new(to_kgsl_pipe(pipe), size);
 	if (!kgsl_ring->bo) {
diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c
index 301ac66..86fc83e 100644
--- a/freedreno/msm/msm_ringbuffer.c
+++ b/freedreno/msm/msm_ringbuffer.c
@@ -40,12 +40,16 @@
  * a backing bo, and a reloc table.
  */
 struct msm_cmd {
+	struct list_head list;
+
 	struct fd_ringbuffer *ring;
 	struct fd_bo *ring_bo;
 
 	/* reloc's table: */
 	struct drm_msm_gem_submit_reloc *relocs;
 	uint32_t nr_relocs, max_relocs;
+
+	uint32_t size;
 };
 
 struct msm_ringbuffer {
@@ -75,10 +79,28 @@ struct msm_ringbuffer {
 	struct msm_cmd **cmds;
 	uint32_t nr_cmds, max_cmds;
 
-	/* current cmd-buffer: */
-	struct msm_cmd *cmd;
+	/* List of physical cmdstream buffers (msm_cmd) assocated with this
+	 * logical fd_ringbuffer.
+	 *
+	 * Note that this is different from msm_ringbuffer::cmds (which
+	 * shadows msm_ringbuffer::submit::cmds for tracking submit ioctl
+	 * related stuff, and *only* is tracked in the parent ringbuffer.
+	 * And only has "completed" cmd buffers (ie. we already know the
+	 * size) added via get_cmd().
+	 */
+	struct list_head cmd_list;
+
+	int is_growable;
+	unsigned cmd_count;
 };
 
+static inline struct msm_ringbuffer * to_msm_ringbuffer(struct fd_ringbuffer *x)
+{
+	return (struct msm_ringbuffer *)x;
+}
+
+#define INIT_SIZE 0x1000
+
 static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
 drm_private extern pthread_mutex_t table_lock;
 
@@ -118,12 +140,15 @@ static void ring_cmd_del(struct msm_cmd *cmd)
 {
 	if (cmd->ring_bo)
 		ring_bo_del(cmd->ring->pipe->dev, cmd->ring_bo);
+	list_del(&cmd->list);
+	to_msm_ringbuffer(cmd->ring)->cmd_count--;
 	free(cmd->relocs);
 	free(cmd);
 }
 
 static struct msm_cmd * ring_cmd_new(struct fd_ringbuffer *ring, uint32_t size)
 {
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
 	struct msm_cmd *cmd = calloc(1, sizeof(*cmd));
 
 	if (!cmd)
@@ -134,6 +159,9 @@ static struct msm_cmd * ring_cmd_new(struct fd_ringbuffer *ring, uint32_t size)
 	if (!cmd->ring_bo)
 		goto fail;
 
+	list_addtail(&cmd->list, &msm_ring->cmd_list);
+	msm_ring->cmd_count++;
+
 	return cmd;
 
 fail:
@@ -158,9 +186,11 @@ static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
 	(x)->nr_ ## name ++; \
 })
 
-static inline struct msm_ringbuffer * to_msm_ringbuffer(struct fd_ringbuffer *x)
+static struct msm_cmd *current_cmd(struct fd_ringbuffer *ring)
 {
-	return (struct msm_ringbuffer *)x;
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+	assert(!LIST_IS_EMPTY(&msm_ring->cmd_list));
+	return LIST_LAST_ENTRY(&msm_ring->cmd_list, struct msm_cmd, list);
 }
 
 static uint32_t append_bo(struct fd_ringbuffer *ring, struct fd_bo *bo)
@@ -248,12 +278,13 @@ static void get_cmd(struct fd_ringbuffer *ring, struct msm_cmd *target_cmd,
 	cmd->submit_offset = submit_offset;
 	cmd->size = size;
 	cmd->pad = 0;
+
+	target_cmd->size = size;
 }
 
 static void * msm_ringbuffer_hostptr(struct fd_ringbuffer *ring)
 {
-	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
-	return fd_bo_map(msm_ring->cmd->ring_bo);
+	return fd_bo_map(current_cmd(ring)->ring_bo);
 }
 
 static uint32_t find_next_reloc_idx(struct msm_cmd *msm_cmd,
@@ -271,6 +302,15 @@ static uint32_t find_next_reloc_idx(struct msm_cmd *msm_cmd,
 	return i;
 }
 
+static void delete_cmds(struct msm_ringbuffer *msm_ring)
+{
+	struct msm_cmd *cmd, *tmp;
+
+	LIST_FOR_EACH_ENTRY_SAFE(cmd, tmp, &msm_ring->cmd_list, list) {
+		ring_cmd_del(cmd);
+	}
+}
+
 static void flush_reset(struct fd_ringbuffer *ring)
 {
 	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
@@ -288,11 +328,36 @@ static void flush_reset(struct fd_ringbuffer *ring)
 		target_cmd->nr_relocs = 0;
 	}
 
-	msm_ring->cmd->nr_relocs = 0;
 	msm_ring->submit.nr_cmds = 0;
 	msm_ring->submit.nr_bos = 0;
 	msm_ring->nr_cmds = 0;
 	msm_ring->nr_bos = 0;
+
+	if (msm_ring->is_growable) {
+		delete_cmds(msm_ring);
+	} else {
+		/* in old mode, just reset the # of relocs: */
+		current_cmd(ring)->nr_relocs = 0;
+	}
+}
+
+static void finalize_current_cmd(struct fd_ringbuffer *ring, uint32_t *last_start)
+{
+	uint32_t submit_offset, size, type;
+	struct fd_ringbuffer *parent;
+
+	if (ring->parent) {
+		parent = ring->parent;
+		type = MSM_SUBMIT_CMD_IB_TARGET_BUF;
+	} else {
+		parent = ring;
+		type = MSM_SUBMIT_CMD_BUF;
+	}
+
+	submit_offset = offset_bytes(last_start, ring->start);
+	size = offset_bytes(ring->cur, last_start);
+
+	get_cmd(parent, current_cmd(ring), submit_offset, size, type);
 }
 
 static void dump_submit(struct msm_ringbuffer *msm_ring)
@@ -323,13 +388,10 @@ static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start
 	struct drm_msm_gem_submit req = {
 			.pipe = to_msm_pipe(ring->pipe)->pipe,
 	};
-	uint32_t i, submit_offset, size;
+	uint32_t i;
 	int ret;
 
-	submit_offset = offset_bytes(last_start, ring->start);
-	size = offset_bytes(ring->cur, last_start);
-
-	get_cmd(ring, msm_ring->cmd, submit_offset, size, MSM_SUBMIT_CMD_BUF);
+	finalize_current_cmd(ring, last_start);
 
 	/* needs to be after get_cmd() as that could create bos/cmds table: */
 	req.bos = VOID2U64(msm_ring->submit.bos),
@@ -367,6 +429,13 @@ static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start
 	return ret;
 }
 
+static void msm_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t size)
+{
+	assert(to_msm_ringbuffer(ring)->is_growable);
+	finalize_current_cmd(ring, ring->last_start);
+	ring_cmd_new(ring, size);
+}
+
 static void msm_ringbuffer_reset(struct fd_ringbuffer *ring)
 {
 	flush_reset(ring);
@@ -375,14 +444,14 @@ static void msm_ringbuffer_reset(struct fd_ringbuffer *ring)
 static void msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
 		const struct fd_reloc *r)
 {
-	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
 	struct fd_ringbuffer *parent = ring->parent ? ring->parent : ring;
 	struct msm_bo *msm_bo = to_msm_bo(r->bo);
 	struct drm_msm_gem_submit_reloc *reloc;
-	uint32_t idx = APPEND(msm_ring->cmd, relocs);
+	struct msm_cmd *cmd = current_cmd(ring);
+	uint32_t idx = APPEND(cmd, relocs);
 	uint32_t addr;
 
-	reloc = &msm_ring->cmd->relocs[idx];
+	reloc = &cmd->relocs[idx];
 
 	reloc->reloc_idx = bo2idx(parent, r->bo, r->flags);
 	reloc->reloc_offset = r->offset;
@@ -398,26 +467,53 @@ static void msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
 	(*ring->cur++) = addr | r->or;
 }
 
-static void msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
-		struct fd_ringbuffer *target,
+static uint32_t msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
+		struct fd_ringbuffer *target, uint32_t cmd_idx,
 		uint32_t submit_offset, uint32_t size)
 {
-	struct msm_cmd *cmd = to_msm_ringbuffer(target)->cmd;
+	struct msm_cmd *cmd = NULL;
+	uint32_t idx = 0;
+
+	LIST_FOR_EACH_ENTRY(cmd, &to_msm_ringbuffer(target)->cmd_list, list) {
+		if (idx == cmd_idx)
+			break;
+		idx++;
+	}
 
-	get_cmd(ring, cmd, submit_offset, size, MSM_SUBMIT_CMD_IB_TARGET_BUF);
+	assert(cmd && (idx == cmd_idx));
+
+	if (idx < (to_msm_ringbuffer(target)->cmd_count - 1)) {
+		/* All but the last cmd buffer is fully "baked" (ie. already has
+		 * done get_cmd() to add it to the cmds table).  But in this case,
+		 * the size we get is invalid (since it is calculated from the
+		 * last cmd buffer):
+		 */
+		size = cmd->size;
+	} else {
+		get_cmd(ring, cmd, submit_offset, size, MSM_SUBMIT_CMD_IB_TARGET_BUF);
+	}
 
 	msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){
 		.bo = cmd->ring_bo,
 		.flags = FD_RELOC_READ,
 		.offset = submit_offset,
 	});
+
+	return size;
+}
+
+static uint32_t msm_ringbuffer_cmd_count(struct fd_ringbuffer *ring)
+{
+	return to_msm_ringbuffer(ring)->cmd_count;
 }
 
 static void msm_ringbuffer_destroy(struct fd_ringbuffer *ring)
 {
 	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
-	if (msm_ring->cmd)
-		ring_cmd_del(msm_ring->cmd);
+
+	flush_reset(ring);
+	delete_cmds(msm_ring);
+
 	free(msm_ring->submit.cmds);
 	free(msm_ring->submit.bos);
 	free(msm_ring->bos);
@@ -428,9 +524,11 @@ static void msm_ringbuffer_destroy(struct fd_ringbuffer *ring)
 static const struct fd_ringbuffer_funcs funcs = {
 		.hostptr = msm_ringbuffer_hostptr,
 		.flush = msm_ringbuffer_flush,
+		.grow = msm_ringbuffer_grow,
 		.reset = msm_ringbuffer_reset,
 		.emit_reloc = msm_ringbuffer_emit_reloc,
 		.emit_reloc_ring = msm_ringbuffer_emit_reloc_ring,
+		.cmd_count = msm_ringbuffer_cmd_count,
 		.destroy = msm_ringbuffer_destroy,
 };
 
@@ -446,15 +544,20 @@ drm_private struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe,
 		goto fail;
 	}
 
+	if (size == 0) {
+		assert(pipe->dev->version >= FD_VERSION_UNLIMITED_CMDS);
+		size = INIT_SIZE;
+		msm_ring->is_growable = TRUE;
+	}
+
+	list_inithead(&msm_ring->cmd_list);
+
 	ring = &msm_ring->base;
 	ring->funcs = &funcs;
+	ring->size = size;
 	ring->pipe = pipe;   /* needed in ring_cmd_new() */
 
-	msm_ring->cmd = ring_cmd_new(ring, size);
-	if (!msm_ring->cmd) {
-		ERROR_MSG("command buffer allocation failed");
-		goto fail;
-	}
+	ring_cmd_new(ring, size);
 
 	return ring;
 fail:
commit d93d697deb4a808890bc9c64ec453b2d2f2ebb7f
Author: Rob Clark <[email protected]>
Date:   Wed Jun 22 10:41:24 2016 -0400

    freedreno/msm: split out dump_submit() helper
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c
index 7eebac1..301ac66 100644
--- a/freedreno/msm/msm_ringbuffer.c
+++ b/freedreno/msm/msm_ringbuffer.c
@@ -295,13 +295,35 @@ static void flush_reset(struct fd_ringbuffer *ring)
 	msm_ring->nr_bos = 0;
 }
 
+static void dump_submit(struct msm_ringbuffer *msm_ring)
+{
+	uint32_t i, j;
+
+	for (i = 0; i < msm_ring->submit.nr_bos; i++) {
+		struct drm_msm_gem_submit_bo *bo = &msm_ring->submit.bos[i];
+		ERROR_MSG("  bos[%d]: handle=%u, flags=%x", i, bo->handle, bo->flags);
+	}
+	for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
+		struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i];
+		struct drm_msm_gem_submit_reloc *relocs = U642VOID(cmd->relocs);
+		ERROR_MSG("  cmd[%d]: type=%u, submit_idx=%u, submit_offset=%u, size=%u",
+				i, cmd->type, cmd->submit_idx, cmd->submit_offset, cmd->size);
+		for (j = 0; j < cmd->nr_relocs; j++) {
+			struct drm_msm_gem_submit_reloc *r = &relocs[j];
+			ERROR_MSG("    reloc[%d]: submit_offset=%u, or=%08x, shift=%d, reloc_idx=%u"
+					", reloc_offset=%"PRIu64, j, r->submit_offset, r->or, r->shift,
+					r->reloc_idx, r->reloc_offset);
+		}
+	}
+}
+
 static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start)
 {
 	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
 	struct drm_msm_gem_submit req = {
 			.pipe = to_msm_pipe(ring->pipe)->pipe,
 	};
-	uint32_t i, j, submit_offset, size;
+	uint32_t i, submit_offset, size;
 	int ret;
 
 	submit_offset = offset_bytes(last_start, ring->start);
@@ -331,23 +353,7 @@ static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start
 			&req, sizeof(req));
 	if (ret) {
 		ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
-		ERROR_MSG("  pipe:  %u", req.pipe);
-		for (i = 0; i < msm_ring->submit.nr_bos; i++) {
-			struct drm_msm_gem_submit_bo *bo = &msm_ring->submit.bos[i];
-			ERROR_MSG("  bos[%d]: handle=%u, flags=%x", i, bo->handle, bo->flags);
-		}
-		for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
-			struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i];
-			struct drm_msm_gem_submit_reloc *relocs = U642VOID(cmd->relocs);
-			ERROR_MSG("  cmd[%d]: type=%u, submit_idx=%u, submit_offset=%u, size=%u",
-					i, cmd->type, cmd->submit_idx, cmd->submit_offset, cmd->size);
-			for (j = 0; j < cmd->nr_relocs; j++) {
-				struct drm_msm_gem_submit_reloc *r = &relocs[j];
-				ERROR_MSG("    reloc[%d]: submit_offset=%u, or=%08x, shift=%d, reloc_idx=%u"
-						", reloc_offset=%"PRIu64, j, r->submit_offset, r->or, r->shift,
-						r->reloc_idx, r->reloc_offset);
-			}
-		}
+		dump_submit(msm_ring);
 	} else if (!ret) {
 		/* update timestamp on all rings associated with submit: */
 		for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
commit 1d1e01b2350e40711fcf19e200e46e2edfd887b2
Author: Rob Clark <[email protected]>
Date:   Tue Jun 21 11:33:30 2016 -0400

    list: add first/last entry macros
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/util_double_list.h b/util_double_list.h
index 27e0761..5d01f52 100644
--- a/util_double_list.h
+++ b/util_double_list.h
@@ -98,6 +98,12 @@ static inline void list_delinit(struct list_head *item)
 #define LIST_ENTRY(__type, __item, __field)   \
     ((__type *)(((char *)(__item)) - offsetof(__type, __field)))
 
+#define LIST_FIRST_ENTRY(__ptr, __type, __field)   \
+    LIST_ENTRY(__type, (__ptr)->next, __field)
+
+#define LIST_LAST_ENTRY(__ptr, __type, __field)   \
+    LIST_ENTRY(__type, (__ptr)->prev, __field)
+
 #define LIST_IS_EMPTY(__list)                   \
     ((__list)->next == (__list))
 
commit dd1f372001d4e6de648fa33b6b74d89aaa24cf75
Author: Rob Clark <[email protected]>
Date:   Fri Jun 3 15:41:20 2016 -0400

    freedreno/msm: split out cmd buffer tracking from ring
    
    First step towards supporting a single logical ringbuffer mapping to
    multiple physical cmd buffers, which will enable dynamically growing
    ringbuffers.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c
index 4b46c20..7eebac1 100644
--- a/freedreno/msm/msm_ringbuffer.c
+++ b/freedreno/msm/msm_ringbuffer.c
@@ -36,11 +36,26 @@
 #include "freedreno_ringbuffer.h"
 #include "msm_priv.h"
 
+/* represents a single cmd buffer in the submit ioctl.  Each cmd buffer has
+ * a backing bo, and a reloc table.
+ */
+struct msm_cmd {
+	struct fd_ringbuffer *ring;
+	struct fd_bo *ring_bo;
+
+	/* reloc's table: */
+	struct drm_msm_gem_submit_reloc *relocs;
+	uint32_t nr_relocs, max_relocs;
+};
+
 struct msm_ringbuffer {
 	struct fd_ringbuffer base;
-	struct fd_bo *ring_bo;
 
-	/* submit ioctl related tables: */
+	/* submit ioctl related tables:
+	 * Note that bos and cmds are tracked by the parent ringbuffer, since
+	 * that is global to the submit ioctl call.  The reloc's table is tracked
+	 * per cmd-buffer.
+	 */
 	struct {
 		/* bo's table: */
 		struct drm_msm_gem_submit_bo *bos;
@@ -49,19 +64,19 @@ struct msm_ringbuffer {
 		/* cmd's table: */
 		struct drm_msm_gem_submit_cmd *cmds;
 		uint32_t nr_cmds, max_cmds;
-
-		/* reloc's table: */
-		struct drm_msm_gem_submit_reloc *relocs;
-		uint32_t nr_relocs, max_relocs;
 	} submit;
 
 	/* should have matching entries in submit.bos: */
+	/* Note, only in parent ringbuffer */
 	struct fd_bo **bos;
 	uint32_t nr_bos, max_bos;
 
 	/* should have matching entries in submit.cmds: */
-	struct fd_ringbuffer **rings;
-	uint32_t nr_rings, max_rings;
+	struct msm_cmd **cmds;
+	uint32_t nr_cmds, max_cmds;
+
+	/* current cmd-buffer: */
+	struct msm_cmd *cmd;
 };
 
 static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -99,6 +114,33 @@ static struct fd_bo * ring_bo_new(struct fd_device *dev, uint32_t size)
 	return bo;
 }
 
+static void ring_cmd_del(struct msm_cmd *cmd)
+{
+	if (cmd->ring_bo)
+		ring_bo_del(cmd->ring->pipe->dev, cmd->ring_bo);
+	free(cmd->relocs);
+	free(cmd);
+}
+
+static struct msm_cmd * ring_cmd_new(struct fd_ringbuffer *ring, uint32_t size)
+{
+	struct msm_cmd *cmd = calloc(1, sizeof(*cmd));
+
+	if (!cmd)
+		return NULL;
+
+	cmd->ring = ring;
+	cmd->ring_bo = ring_bo_new(ring->pipe->dev, size);
+	if (!cmd->ring_bo)
+		goto fail;
+
+	return cmd;
+
+fail:
+	ring_cmd_del(cmd);
+	return NULL;
+}
+
 static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
 {
 	if ((nr + 1) > *max) {
@@ -179,8 +221,7 @@ static int check_cmd_bo(struct fd_ringbuffer *ring,
 /* Ensure that submit has corresponding entry in cmds table for the
  * target cmdstream buffer:
  */
-static void get_cmd(struct fd_ringbuffer *ring,
-		struct fd_ringbuffer *target_ring, struct fd_bo *target_bo,
+static void get_cmd(struct fd_ringbuffer *ring, struct msm_cmd *target_cmd,
 		uint32_t submit_offset, uint32_t size, uint32_t type)
 {
 	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
@@ -193,17 +234,17 @@ static void get_cmd(struct fd_ringbuffer *ring,
 		if ((cmd->submit_offset == submit_offset) &&
 				(cmd->size == size) &&
 				(cmd->type == type) &&
-				check_cmd_bo(ring, cmd, target_bo))
+				check_cmd_bo(ring, cmd, target_cmd->ring_bo))
 			return;
 	}
 
 	/* create cmd buf if not: */
 	i = APPEND(&msm_ring->submit, cmds);
-	APPEND(msm_ring, rings);
-	msm_ring->rings[i] = target_ring;
+	APPEND(msm_ring, cmds);
+	msm_ring->cmds[i] = target_cmd;
 	cmd = &msm_ring->submit.cmds[i];
 	cmd->type = type;
-	cmd->submit_idx = bo2idx(ring, target_bo, FD_RELOC_READ);
+	cmd->submit_idx = bo2idx(ring, target_cmd->ring_bo, FD_RELOC_READ);
 	cmd->submit_offset = submit_offset;
 	cmd->size = size;
 	cmd->pad = 0;
@@ -212,17 +253,17 @@ static void get_cmd(struct fd_ringbuffer *ring,
 static void * msm_ringbuffer_hostptr(struct fd_ringbuffer *ring)
 {
 	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
-	return fd_bo_map(msm_ring->ring_bo);
+	return fd_bo_map(msm_ring->cmd->ring_bo);
 }
 
-static uint32_t find_next_reloc_idx(struct msm_ringbuffer *msm_ring,
+static uint32_t find_next_reloc_idx(struct msm_cmd *msm_cmd,
 		uint32_t start, uint32_t offset)
 {
 	uint32_t i;
 
 	/* a binary search would be more clever.. */
-	for (i = start; i < msm_ring->submit.nr_relocs; i++) {
-		struct drm_msm_gem_submit_reloc *reloc = &msm_ring->submit.relocs[i];
+	for (i = start; i < msm_cmd->nr_relocs; i++) {
+		struct drm_msm_gem_submit_reloc *reloc = &msm_cmd->relocs[i];
 		if (reloc->submit_offset >= offset)
 			return i;
 	}
@@ -243,21 +284,20 @@ static void flush_reset(struct fd_ringbuffer *ring)
 
 	/* for each of the cmd buffers, clear their reloc's: */
 	for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
-		struct msm_ringbuffer *target_ring = to_msm_ringbuffer(msm_ring->rings[i]);
-		target_ring->submit.nr_relocs = 0;
+		struct msm_cmd *target_cmd = msm_ring->cmds[i];
+		target_cmd->nr_relocs = 0;
 	}
 
-	msm_ring->submit.nr_relocs = 0;
+	msm_ring->cmd->nr_relocs = 0;
 	msm_ring->submit.nr_cmds = 0;
 	msm_ring->submit.nr_bos = 0;
-	msm_ring->nr_rings = 0;
+	msm_ring->nr_cmds = 0;
 	msm_ring->nr_bos = 0;
 }
 
 static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start)
 {
 	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
-	struct fd_bo *ring_bo = msm_ring->ring_bo;
 	struct drm_msm_gem_submit req = {
 			.pipe = to_msm_pipe(ring->pipe)->pipe,
 	};
@@ -267,7 +307,7 @@ static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start
 	submit_offset = offset_bytes(last_start, ring->start);
 	size = offset_bytes(ring->cur, last_start);
 
-	get_cmd(ring, ring, ring_bo, submit_offset, size, MSM_SUBMIT_CMD_BUF);
+	get_cmd(ring, msm_ring->cmd, submit_offset, size, MSM_SUBMIT_CMD_BUF);
 
 	/* needs to be after get_cmd() as that could create bos/cmds table: */
 	req.bos = VOID2U64(msm_ring->submit.bos),
@@ -278,10 +318,10 @@ static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start
 	/* for each of the cmd's fix up their reloc's: */
 	for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
 		struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i];
-		struct msm_ringbuffer *target_ring = to_msm_ringbuffer(msm_ring->rings[i]);
-		uint32_t a = find_next_reloc_idx(target_ring, 0, cmd->submit_offset);
-		uint32_t b = find_next_reloc_idx(target_ring, a, cmd->submit_offset + cmd->size);
-		cmd->relocs = VOID2U64(&target_ring->submit.relocs[a]);
+		struct msm_cmd *msm_cmd = msm_ring->cmds[i];
+		uint32_t a = find_next_reloc_idx(msm_cmd, 0, cmd->submit_offset);
+		uint32_t b = find_next_reloc_idx(msm_cmd, a, cmd->submit_offset + cmd->size);
+		cmd->relocs = VOID2U64(&msm_cmd->relocs[a]);
 		cmd->nr_relocs = (b > a) ? b - a : 0;
 	}
 
@@ -308,12 +348,11 @@ static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start
 						r->reloc_idx, r->reloc_offset);
 			}
 		}
-	} else {
+	} else if (!ret) {
 		/* update timestamp on all rings associated with submit: */
 		for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
-			struct fd_ringbuffer *target_ring = msm_ring->rings[i];
-			if (!ret)
-				target_ring->last_timestamp = req.fence;
+			struct msm_cmd *msm_cmd = msm_ring->cmds[i];
+			msm_cmd->ring->last_timestamp = req.fence;
 		}
 	}
 
@@ -334,10 +373,10 @@ static void msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
 	struct fd_ringbuffer *parent = ring->parent ? ring->parent : ring;
 	struct msm_bo *msm_bo = to_msm_bo(r->bo);
 	struct drm_msm_gem_submit_reloc *reloc;
-	uint32_t idx = APPEND(&msm_ring->submit, relocs);
+	uint32_t idx = APPEND(msm_ring->cmd, relocs);
 	uint32_t addr;
 
-	reloc = &msm_ring->submit.relocs[idx];
+	reloc = &msm_ring->cmd->relocs[idx];
 
 	reloc->reloc_idx = bo2idx(parent, r->bo, r->flags);
 	reloc->reloc_offset = r->offset;
@@ -357,13 +396,12 @@ static void msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
 		struct fd_ringbuffer *target,
 		uint32_t submit_offset, uint32_t size)
 {
-	struct fd_bo *target_bo = to_msm_ringbuffer(target)->ring_bo;
+	struct msm_cmd *cmd = to_msm_ringbuffer(target)->cmd;
 
-	get_cmd(ring, target, target_bo, submit_offset, size,
-			MSM_SUBMIT_CMD_IB_TARGET_BUF);
+	get_cmd(ring, cmd, submit_offset, size, MSM_SUBMIT_CMD_IB_TARGET_BUF);
 
 	msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){
-		.bo = target_bo,
+		.bo = cmd->ring_bo,
 		.flags = FD_RELOC_READ,
 		.offset = submit_offset,
 	});
@@ -372,13 +410,12 @@ static void msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
 static void msm_ringbuffer_destroy(struct fd_ringbuffer *ring)
 {
 	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
-	if (msm_ring->ring_bo)
-		ring_bo_del(ring->pipe->dev, msm_ring->ring_bo);
-	free(msm_ring->submit.relocs);
+	if (msm_ring->cmd)
+		ring_cmd_del(msm_ring->cmd);
 	free(msm_ring->submit.cmds);
 	free(msm_ring->submit.bos);
 	free(msm_ring->bos);
-	free(msm_ring->rings);
+	free(msm_ring->cmds);
 	free(msm_ring);
 }
 
@@ -405,10 +442,11 @@ drm_private struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe,
 
 	ring = &msm_ring->base;
 	ring->funcs = &funcs;
+	ring->pipe = pipe;   /* needed in ring_cmd_new() */
 
-	msm_ring->ring_bo = ring_bo_new(pipe->dev, size);
-	if (!msm_ring->ring_bo) {
-		ERROR_MSG("ringbuffer allocation failed");
+	msm_ring->cmd = ring_cmd_new(ring, size);
+	if (!msm_ring->cmd) {
+		ERROR_MSG("command buffer allocation failed");
 		goto fail;
 	}
 
commit 0d6152913098d739be07f9085cdf44c9cc68d096
Author: Rob Clark <[email protected]>
Date:   Sat Jun 18 09:08:53 2016 -0400

    freedreno/msm: drop return from get_cmd()
    
    Not actually needed.  It just needs to ensure that there is a
    corresponding entry in the submit's cmds table.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c
index 66ae146..4b46c20 100644
--- a/freedreno/msm/msm_ringbuffer.c
+++ b/freedreno/msm/msm_ringbuffer.c
@@ -176,12 +176,15 @@ static int check_cmd_bo(struct fd_ringbuffer *ring,
 	return msm_ring->submit.bos[cmd->submit_idx].handle == bo->handle;
 }
 
-static struct drm_msm_gem_submit_cmd * get_cmd(struct fd_ringbuffer *ring,
+/* Ensure that submit has corresponding entry in cmds table for the
+ * target cmdstream buffer:
+ */
+static void get_cmd(struct fd_ringbuffer *ring,
 		struct fd_ringbuffer *target_ring, struct fd_bo *target_bo,
 		uint32_t submit_offset, uint32_t size, uint32_t type)
 {
 	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
-	struct drm_msm_gem_submit_cmd *cmd = NULL;
+	struct drm_msm_gem_submit_cmd *cmd;
 	uint32_t i;
 
 	/* figure out if we already have a cmd buf: */
@@ -191,24 +194,19 @@ static struct drm_msm_gem_submit_cmd * get_cmd(struct fd_ringbuffer *ring,
 				(cmd->size == size) &&
 				(cmd->type == type) &&
 				check_cmd_bo(ring, cmd, target_bo))
-			break;
-		cmd = NULL;
+			return;
 	}
 
 	/* create cmd buf if not: */
-	if (!cmd) {
-		uint32_t idx = APPEND(&msm_ring->submit, cmds);
-		APPEND(msm_ring, rings);
-		msm_ring->rings[idx] = target_ring;
-		cmd = &msm_ring->submit.cmds[idx];
-		cmd->type = type;
-		cmd->submit_idx = bo2idx(ring, target_bo, FD_RELOC_READ);
-		cmd->submit_offset = submit_offset;
-		cmd->size = size;
-		cmd->pad = 0;
-	}
-
-	return cmd;
+	i = APPEND(&msm_ring->submit, cmds);
+	APPEND(msm_ring, rings);
+	msm_ring->rings[i] = target_ring;
+	cmd = &msm_ring->submit.cmds[i];
+	cmd->type = type;
+	cmd->submit_idx = bo2idx(ring, target_bo, FD_RELOC_READ);
+	cmd->submit_offset = submit_offset;
+	cmd->size = size;
+	cmd->pad = 0;
 }
 
 static void * msm_ringbuffer_hostptr(struct fd_ringbuffer *ring)
@@ -360,11 +358,9 @@ static void msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
 		uint32_t submit_offset, uint32_t size)
 {
 	struct fd_bo *target_bo = to_msm_ringbuffer(target)->ring_bo;
-	struct drm_msm_gem_submit_cmd *cmd;
 
-	cmd = get_cmd(ring, target, target_bo, submit_offset, size,
+	get_cmd(ring, target, target_bo, submit_offset, size,
 			MSM_SUBMIT_CMD_IB_TARGET_BUF);
-	assert(cmd);
 
 	msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){
 		.bo = target_bo,
commit 892141a321c7acd32000e145916217eda2da14bb
Author: Rob Clark <[email protected]>
Date:   Wed Jun 1 16:11:52 2016 -0400

    freedreno/msm: use private bo-cache for ringbuffer bo's
    
    Since they get vmap'd on the kernel side, they are a bit more costly.
    Don't let them mingle with the riffraff.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/msm/msm_device.c b/freedreno/msm/msm_device.c
index 25c097c..727baa4 100644
--- a/freedreno/msm/msm_device.c
+++ b/freedreno/msm/msm_device.c
@@ -39,6 +39,7 @@
 static void msm_device_destroy(struct fd_device *dev)
 {
 	struct msm_device *msm_dev = to_msm_device(dev);
+	fd_bo_cache_cleanup(&msm_dev->ring_cache, 0);
 	free(msm_dev);
 }
 
@@ -61,5 +62,7 @@ drm_private struct fd_device * msm_device_new(int fd)
 	dev = &msm_dev->base;
 	dev->funcs = &funcs;
 
+	fd_bo_cache_init(&msm_dev->ring_cache, TRUE);
+
 	return dev;
 }
diff --git a/freedreno/msm/msm_priv.h b/freedreno/msm/msm_priv.h
index e499b3b..1f44398 100644
--- a/freedreno/msm/msm_priv.h
+++ b/freedreno/msm/msm_priv.h
@@ -39,6 +39,7 @@
 
 struct msm_device {
 	struct fd_device base;
+	struct fd_bo_cache ring_cache;
 };
 
 static inline struct msm_device * to_msm_device(struct fd_device *x)
diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c
index 32ed8b4..66ae146 100644
--- a/freedreno/msm/msm_ringbuffer.c
+++ b/freedreno/msm/msm_ringbuffer.c
@@ -65,6 +65,39 @@ struct msm_ringbuffer {
 };
 
 static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
+drm_private extern pthread_mutex_t table_lock;
+
+static void ring_bo_del(struct fd_device *dev, struct fd_bo *bo)
+{
+	int ret;
+
+	pthread_mutex_lock(&table_lock);
+	ret = fd_bo_cache_free(&to_msm_device(dev)->ring_cache, bo);
+	pthread_mutex_unlock(&table_lock);
+
+	if (ret == 0)
+		return;
+
+	fd_bo_del(bo);
+}
+
+static struct fd_bo * ring_bo_new(struct fd_device *dev, uint32_t size)
+{
+	struct fd_bo *bo;
+
+	bo = fd_bo_cache_alloc(&to_msm_device(dev)->ring_cache, &size, 0);
+	if (bo)
+		return bo;
+
+	bo = fd_bo_new(dev, size, 0);
+	if (!bo)
+		return NULL;
+
+	/* keep ringbuffer bo's out of the normal bo cache: */
+	bo->bo_reuse = FALSE;
+
+	return bo;
+}
 
 static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
 {
@@ -344,7 +377,7 @@ static void msm_ringbuffer_destroy(struct fd_ringbuffer *ring)
 {
 	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
 	if (msm_ring->ring_bo)
-		fd_bo_del(msm_ring->ring_bo);
+		ring_bo_del(ring->pipe->dev, msm_ring->ring_bo);
 	free(msm_ring->submit.relocs);
 	free(msm_ring->submit.cmds);
 	free(msm_ring->submit.bos);
@@ -377,7 +410,7 @@ drm_private struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe,
 	ring = &msm_ring->base;
 	ring->funcs = &funcs;
 
-	msm_ring->ring_bo = fd_bo_new(pipe->dev, size, 0);
+	msm_ring->ring_bo = ring_bo_new(pipe->dev, size);
 	if (!msm_ring->ring_bo) {
 		ERROR_MSG("ringbuffer allocation failed");
 		goto fail;
commit 19b82b9817b696cfe06d32340cb65231775b203b
Author: Rob Clark <[email protected]>
Date:   Wed Jun 1 16:04:13 2016 -0400

    freedreno: fix potential leak at free
    
    If user has emit'd reloc's, and then resets or deletes the ring, we want
    to drop the ref's that the ring holds to the bo's to avoid a leak.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_ringbuffer.c b/freedreno/freedreno_ringbuffer.c
index ab5d31f..34a06d8 100644
--- a/freedreno/freedreno_ringbuffer.c
+++ b/freedreno/freedreno_ringbuffer.c
@@ -57,6 +57,7 @@ fd_ringbuffer_new(struct fd_pipe *pipe, uint32_t size)
 
 void fd_ringbuffer_del(struct fd_ringbuffer *ring)
 {
+	fd_ringbuffer_reset(ring);
 	ring->funcs->destroy(ring);
 }
 
diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c
index b5a50de..32ed8b4 100644
--- a/freedreno/msm/msm_ringbuffer.c
+++ b/freedreno/msm/msm_ringbuffer.c
@@ -204,6 +204,12 @@ static void flush_reset(struct fd_ringbuffer *ring)
 	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
 	unsigned i;
 
+	for (i = 0; i < msm_ring->nr_bos; i++) {
+		struct msm_bo *msm_bo = to_msm_bo(msm_ring->bos[i]);
+		msm_bo->current_ring = NULL;
+		fd_bo_del(&msm_bo->base);
+	}
+
 	/* for each of the cmd buffers, clear their reloc's: */
 	for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
 		struct msm_ringbuffer *target_ring = to_msm_ringbuffer(msm_ring->rings[i]);
@@ -280,12 +286,6 @@ static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start
 		}
 	}
 
-	for (i = 0; i < msm_ring->nr_bos; i++) {
-		struct msm_bo *msm_bo = to_msm_bo(msm_ring->bos[i]);
-		msm_bo->current_ring = NULL;
-		fd_bo_del(&msm_bo->base);
-	}
-
 	flush_reset(ring);
 
 	return ret;
commit 9e697c74991bd265a549bedfc7cd4a0267e234df
Author: Rob Clark <[email protected]>
Date:   Wed Jun 1 15:42:55 2016 -0400

    freedreno: ocd
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_bo.c b/freedreno/freedreno_bo.c
index cf2d7cb..996d6b9 100644
--- a/freedreno/freedreno_bo.c
+++ b/freedreno/freedreno_bo.c
@@ -99,7 +99,7 @@ fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
 
 	pthread_mutex_lock(&table_lock);
 	bo = bo_from_handle(dev, size, handle);
-	bo->bo_reuse = 1;
+	bo->bo_reuse = TRUE;
 	pthread_mutex_unlock(&table_lock);
 
 	return bo;
@@ -249,7 +249,7 @@ int fd_bo_get_name(struct fd_bo *bo, uint32_t *name)
 		pthread_mutex_lock(&table_lock);
 		set_name(bo, req.name);
 		pthread_mutex_unlock(&table_lock);
-		bo->bo_reuse = 0;
+		bo->bo_reuse = FALSE;
 	}
 
 	*name = bo->name;
@@ -273,7 +273,7 @@ int fd_bo_dmabuf(struct fd_bo *bo)
 		return ret;
 	}
 
-	bo->bo_reuse = 0;
+	bo->bo_reuse = FALSE;
 
 	return prime_fd;
 }
diff --git a/freedreno/freedreno_ringbuffer.c b/freedreno/freedreno_ringbuffer.c
index be80296..ab5d31f 100644
--- a/freedreno/freedreno_ringbuffer.c
+++ b/freedreno/freedreno_ringbuffer.c
@@ -97,10 +97,8 @@ void fd_ringbuffer_reloc(struct fd_ringbuffer *ring,
 	ring->funcs->emit_reloc(ring, reloc);
 }
 
-void
-fd_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
-			      struct fd_ringmarker *target,
-			      struct fd_ringmarker *end)
+void fd_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
+		struct fd_ringmarker *target, struct fd_ringmarker *end)
 {
 	uint32_t submit_offset, size;
 
commit 8a6a8512d4ac6db5e85911de81f156d325dcc343
Author: Rob Clark <[email protected]>
Date:   Wed Jun 1 15:37:52 2016 -0400

    freedreno: support either coarse or fine-grained bucket sizes
    
    The normal bo cache uses some intermediate steps between power of two
    jumps to reduce memory wastage.  But for a ringbuffer bo cache, we do
    not need this.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_bo_cache.c b/freedreno/freedreno_bo_cache.c
index 58d171e..7becb0d 100644
--- a/freedreno/freedreno_bo_cache.c
+++ b/freedreno/freedreno_bo_cache.c
@@ -49,8 +49,12 @@ add_bucket(struct fd_bo_cache *cache, int size)
 	cache->num_buckets++;
 }
 
+/**
+ * @coarse: if true, only power-of-two bucket sizes, otherwise
+ *    fill in for a bit smoother size curve..
+ */
 drm_private void
-fd_bo_cache_init(struct fd_bo_cache *cache)
+fd_bo_cache_init(struct fd_bo_cache *cache, int course)
 {
 	unsigned long size, cache_max_size = 64 * 1024 * 1024;
 
@@ -64,14 +68,17 @@ fd_bo_cache_init(struct fd_bo_cache *cache)
 	 */
 	add_bucket(cache, 4096);
 	add_bucket(cache, 4096 * 2);
-	add_bucket(cache, 4096 * 3);
+	if (!course)
+		add_bucket(cache, 4096 * 3);
 
 	/* Initialize the linked lists for BO reuse cache. */
 	for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
 		add_bucket(cache, size);
-		add_bucket(cache, size + size * 1 / 4);
-		add_bucket(cache, size + size * 2 / 4);
-		add_bucket(cache, size + size * 3 / 4);
+		if (!course) {
+			add_bucket(cache, size + size * 1 / 4);
+			add_bucket(cache, size + size * 2 / 4);
+			add_bucket(cache, size + size * 3 / 4);
+		}
 	}
 }
 
diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c
index b99bce2..fcbf140 100644
--- a/freedreno/freedreno_device.c
+++ b/freedreno/freedreno_device.c
@@ -85,7 +85,7 @@ out:
 	dev->fd = fd;
 	dev->handle_table = drmHashCreate();
 	dev->name_table = drmHashCreate();
-	fd_bo_cache_init(&dev->bo_cache);
+	fd_bo_cache_init(&dev->bo_cache, FALSE);
 
 	return dev;
 }
diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h
index 5e8f03d..9737b32 100644
--- a/freedreno/freedreno_priv.h
+++ b/freedreno/freedreno_priv.h
@@ -104,7 +104,7 @@ struct fd_device {
 	int closefd;        /* call close(fd) upon destruction */
 };
 
-drm_private void fd_bo_cache_init(struct fd_bo_cache *cache);
+drm_private void fd_bo_cache_init(struct fd_bo_cache *cache, int coarse);
 drm_private void fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time);
 drm_private struct fd_bo * fd_bo_cache_alloc(struct fd_bo_cache *cache,
 		uint32_t *size, uint32_t flags);
commit 904f1361ae11d53ee4d0cf297d38f4c243ee8d69
Author: Rob Clark <[email protected]>
Date:   Wed Jun 1 14:35:44 2016 -0400

    freedreno: expose kernel driver version
    
    gallium needs to know if the kernel is new enough to support explicit
    fencing, dynamically grown ringbuffers, etc.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c
index 3da5c7c..b99bce2 100644
--- a/freedreno/freedreno_device.c
+++ b/freedreno/freedreno_device.c
@@ -140,3 +140,8 @@ int fd_device_fd(struct fd_device *dev)
 {
 	return dev->fd;
 }
+
+enum fd_version fd_device_version(struct fd_device *dev)
+{
+	return dev->version;
+}
diff --git a/freedreno/freedreno_drmif.h b/freedreno/freedreno_drmif.h
index 15ae075..af5e1da 100644
--- a/freedreno/freedreno_drmif.h
+++ b/freedreno/freedreno_drmif.h
@@ -89,6 +89,11 @@ struct fd_device * fd_device_ref(struct fd_device *dev);
 void fd_device_del(struct fd_device *dev);
 int fd_device_fd(struct fd_device *dev);
 
+enum fd_version {
+	FD_VERSION_MADVISE = 1,            /* kernel supports madvise */
+	FD_VERSION_UNLIMITED_CMDS = 1,     /* submits w/ >4 cmd buffers (growable ringbuffer) */
+};
+enum fd_version fd_device_version(struct fd_device *dev);
 
 /* pipe functions:
  */
diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h
index f3ddd77..5e8f03d 100644
--- a/freedreno/freedreno_priv.h
+++ b/freedreno/freedreno_priv.h
@@ -83,7 +83,7 @@ struct fd_bo_cache {
 
 struct fd_device {
 	int fd;
-	int version;
+	enum fd_version version;
 	atomic_t refcnt;
 
 	/* tables to keep track of bo's, to avoid "evil-twin" fd_bo objects:
diff --git a/freedreno/msm/msm_bo.c b/freedreno/msm/msm_bo.c
index cfaec82..72471df 100644
--- a/freedreno/msm/msm_bo.c
+++ b/freedreno/msm/msm_bo.c
@@ -98,7 +98,7 @@ static int msm_bo_madvise(struct fd_bo *bo, int willneed)
 	int ret;
 
 	/* older kernels do not support this: */
-	if (bo->dev->version < 1)
+	if (bo->dev->version < FD_VERSION_MADVISE)
 		return willneed;
 
 	ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_MADVISE, &req, sizeof(req));
commit fe07584e050db55d0e41ed52c0c42f5e8ae84315
Author: Rob Clark <[email protected]>
Date:   Wed Jun 1 14:35:06 2016 -0400

    freedreno: fix potential fd leak in error path
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c
index 027414e..3da5c7c 100644
--- a/freedreno/freedreno_device.c
+++ b/freedreno/freedreno_device.c
@@ -95,9 +95,12 @@ out:
  */
 struct fd_device * fd_device_new_dup(int fd)
 {
-	struct fd_device *dev = fd_device_new(dup(fd));
+	int dup_fd = dup(fd);
+	struct fd_device *dev = fd_device_new(dup_fd);
 	if (dev)
 		dev->closefd = 1;
+	else
+		close(dup_fd);
 	return dev;
 }
 
commit eb846d46bca614f24c50f3fa89f94a6820e16589
Author: Rob Clark <[email protected]>
Date:   Tue May 31 12:06:50 2016 -0400

    freedreno: add madvise support
    
    With a new enough drm/msm, we can let the kernel know about buffers that
    are in the bo cache, so the kernel can free them under memory pressure.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_bo_cache.c b/freedreno/freedreno_bo_cache.c
index 17199d2..58d171e 100644
--- a/freedreno/freedreno_bo_cache.c
+++ b/freedreno/freedreno_bo_cache.c
@@ -165,10 +165,18 @@ fd_bo_cache_alloc(struct fd_bo_cache *cache, uint32_t *size, uint32_t flags)
 	bucket = get_bucket(cache, *size);
 
 	/* see if we can be green and recycle: */
+retry:
 	if (bucket) {
 		*size = bucket->size;
 		bo = find_in_bucket(bucket, flags);
 		if (bo) {
+			if (bo->funcs->madvise(bo, TRUE) <= 0) {
+				/* we've lost the backing pages, delete and try again: */
+				pthread_mutex_lock(&table_lock);
+				bo_del(bo);
+				pthread_mutex_unlock(&table_lock);
+				goto retry;
+			}
 			atomic_set(&bo->refcnt, 1);
 			fd_device_ref(bo->dev);
 			return bo;
@@ -187,6 +195,8 @@ fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo)
 	if (bucket) {
 		struct timespec time;
 
+		bo->funcs->madvise(bo, FALSE);
+
 		clock_gettime(CLOCK_MONOTONIC, &time);
 
 		bo->free_time = time.tv_sec;
diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c
index 15e41f0..027414e 100644
--- a/freedreno/freedreno_device.c
+++ b/freedreno/freedreno_device.c
@@ -56,7 +56,15 @@ struct fd_device * fd_device_new(int fd)
 
 	if (!strcmp(version->name, "msm")) {
 		DEBUG_MSG("msm DRM device");
+		if (version->version_major != 1) {
+			ERROR_MSG("unsupported version: %u.%u.%u", version->version_major,
+				version->version_minor, version->version_patchlevel);
+			dev = NULL;
+			goto out;
+		}
+
 		dev = msm_device_new(fd);
+		dev->version = version->version_minor;
 #ifdef HAVE_FREEDRENO_KGSL
 	} else if (!strcmp(version->name, "kgsl")) {
 		DEBUG_MSG("kgsl DRM device");
@@ -66,6 +74,8 @@ struct fd_device * fd_device_new(int fd)
 		ERROR_MSG("unknown device: %s", version->name);
 		dev = NULL;
 	}
+
+out:
 	drmFreeVersion(version);
 
 	if (!dev)
diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h
index 4159e52..f3ddd77 100644
--- a/freedreno/freedreno_priv.h
+++ b/freedreno/freedreno_priv.h
@@ -54,6 +54,13 @@
 #include "freedreno_ringbuffer.h"
 #include "drm.h"
 
+#ifndef TRUE
+#  define TRUE 1
+#endif
+#ifndef FALSE
+#  define FALSE 0
+#endif
+
 struct fd_device_funcs {
 	int (*bo_new_handle)(struct fd_device *dev, uint32_t size,
 			uint32_t flags, uint32_t *handle);
@@ -76,6 +83,7 @@ struct fd_bo_cache {
 
 struct fd_device {
 	int fd;
+	int version;
 	atomic_t refcnt;
 
 	/* tables to keep track of bo's, to avoid "evil-twin" fd_bo objects:
@@ -139,6 +147,7 @@ struct fd_bo_funcs {
 	int (*offset)(struct fd_bo *bo, uint64_t *offset);
 	int (*cpu_prep)(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op);
 	void (*cpu_fini)(struct fd_bo *bo);
+	int (*madvise)(struct fd_bo *bo, int willneed);
 	void (*destroy)(struct fd_bo *bo);
 };
 
diff --git a/freedreno/kgsl/kgsl_bo.c b/freedreno/kgsl/kgsl_bo.c
index 2b45b5e..ab3485e 100644
--- a/freedreno/kgsl/kgsl_bo.c
+++ b/freedreno/kgsl/kgsl_bo.c
@@ -116,6 +116,11 @@ static void kgsl_bo_cpu_fini(struct fd_bo *bo)
 {
 }
 
+static int kgsl_bo_madvise(struct fd_bo *bo, int willneed)
+{
+	return willneed; /* not supported by kgsl */
+}
+
 static void kgsl_bo_destroy(struct fd_bo *bo)
 {
 	struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
@@ -127,6 +132,7 @@ static const struct fd_bo_funcs funcs = {
 		.offset = kgsl_bo_offset,
 		.cpu_prep = kgsl_bo_cpu_prep,
 		.cpu_fini = kgsl_bo_cpu_fini,
+		.madvise = kgsl_bo_madvise,
 		.destroy = kgsl_bo_destroy,
 };
 
diff --git a/freedreno/msm/msm_bo.c b/freedreno/msm/msm_bo.c
index cd05a6c..cfaec82 100644
--- a/freedreno/msm/msm_bo.c
+++ b/freedreno/msm/msm_bo.c
@@ -89,6 +89,25 @@ static void msm_bo_cpu_fini(struct fd_bo *bo)
 	drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_CPU_FINI, &req, sizeof(req));
 }
 
+static int msm_bo_madvise(struct fd_bo *bo, int willneed)
+{
+	struct drm_msm_gem_madvise req = {
+			.handle = bo->handle,
+			.madv = willneed ? MSM_MADV_WILLNEED : MSM_MADV_DONTNEED,
+	};
+	int ret;
+
+	/* older kernels do not support this: */
+	if (bo->dev->version < 1)
+		return willneed;
+
+	ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_MADVISE, &req, sizeof(req));
+	if (ret)
+		return ret;
+
+	return req.retained;
+}
+
 static void msm_bo_destroy(struct fd_bo *bo)
 {
 	struct msm_bo *msm_bo = to_msm_bo(bo);
@@ -100,6 +119,7 @@ static const struct fd_bo_funcs funcs = {
 		.offset = msm_bo_offset,
 		.cpu_prep = msm_bo_cpu_prep,
 		.cpu_fini = msm_bo_cpu_fini,
+		.madvise = msm_bo_madvise,
 		.destroy = msm_bo_destroy,
 };
 
commit 0c270df8dfde6d6d7b7adb236cd3325f2c0115bd
Author: Rob Clark <[email protected]>
Date:   Tue May 31 11:49:46 2016 -0400

    freedreno: sync uapi
    
    (from drm-next for 4.8)
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/msm/msm_drm.h b/freedreno/msm/msm_drm.h
index 6a2a265..cbf75c3 100644
--- a/freedreno/msm/msm_drm.h
+++ b/freedreno/msm/msm_drm.h
@@ -28,9 +28,13 @@
 #include <stddef.h>
 #include "drm.h"
 
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
 /* Please note that modifications to all structs defined here are
  * subject to backwards-compatibility constraints:
- *  1) Do not use pointers, use uint64_t instead for 32 bit / 64 bit
+ *  1) Do not use pointers, use __u64 instead for 32 bit / 64 bit
  *     user/kernel compatibility
  *  2) Keep fields aligned to their size
  *  3) Because of how drm_ioctl() works, we can add new fields at
@@ -51,8 +55,8 @@
  * same as 'struct timespec' but 32/64b ABI safe.
  */
 struct drm_msm_timespec {
-	int64_t tv_sec;          /* seconds */
-	int64_t tv_nsec;         /* nanoseconds */
+	__s64 tv_sec;          /* seconds */
+	__s64 tv_nsec;         /* nanoseconds */
 };
 
 #define MSM_PARAM_GPU_ID     0x01
@@ -62,9 +66,9 @@ struct drm_msm_timespec {
 #define MSM_PARAM_TIMESTAMP  0x05
 
 struct drm_msm_param {
-	uint32_t pipe;           /* in, MSM_PIPE_x */
-	uint32_t param;          /* in, MSM_PARAM_x */
-	uint64_t value;          /* out (get_param) or in (set_param) */
+	__u32 pipe;           /* in, MSM_PIPE_x */
+	__u32 param;          /* in, MSM_PARAM_x */
+	__u64 value;          /* out (get_param) or in (set_param) */
 };
 
 /*
@@ -86,15 +90,15 @@ struct drm_msm_param {
                               MSM_BO_UNCACHED)
 
 struct drm_msm_gem_new {
-	uint64_t size;           /* in */
-	uint32_t flags;          /* in, mask of MSM_BO_x */
-	uint32_t handle;         /* out */
+	__u64 size;           /* in */
+	__u32 flags;          /* in, mask of MSM_BO_x */
+	__u32 handle;         /* out */
 };
 
 struct drm_msm_gem_info {
-	uint32_t handle;         /* in */
-	uint32_t pad;
-	uint64_t offset;         /* out, offset to pass to mmap() */
+	__u32 handle;         /* in */
+	__u32 pad;
+	__u64 offset;         /* out, offset to pass to mmap() */
 };
 
 #define MSM_PREP_READ        0x01
@@ -104,13 +108,13 @@ struct drm_msm_gem_info {
 #define MSM_PREP_FLAGS       (MSM_PREP_READ | MSM_PREP_WRITE | MSM_PREP_NOSYNC)
 
 struct drm_msm_gem_cpu_prep {
-	uint32_t handle;         /* in */
-	uint32_t op;             /* in, mask of MSM_PREP_x */
+	__u32 handle;         /* in */
+	__u32 op;             /* in, mask of MSM_PREP_x */
 	struct drm_msm_timespec timeout;   /* in */
 };
 
 struct drm_msm_gem_cpu_fini {
-	uint32_t handle;         /* in */
+	__u32 handle;         /* in */
 };
 
 /*
@@ -129,11 +133,11 @@ struct drm_msm_gem_cpu_fini {
  * otherwise EINVAL.
  */
 struct drm_msm_gem_submit_reloc {
-	uint32_t submit_offset;  /* in, offset from submit_bo */
-	uint32_t or;             /* in, value OR'd with result */
-	int32_t  shift;          /* in, amount of left shift (can be negative) */
-	uint32_t reloc_idx;      /* in, index of reloc_bo buffer */
-	uint64_t reloc_offset;   /* in, offset from start of reloc_bo */
+	__u32 submit_offset;  /* in, offset from submit_bo */
+	__u32 or;             /* in, value OR'd with result */
+	__s32 shift;          /* in, amount of left shift (can be negative) */
+	__u32 reloc_idx;      /* in, index of reloc_bo buffer */
+	__u64 reloc_offset;   /* in, offset from start of reloc_bo */
 };
 
 /* submit-types:
@@ -148,13 +152,13 @@ struct drm_msm_gem_submit_reloc {
 #define MSM_SUBMIT_CMD_IB_TARGET_BUF   0x0002
 #define MSM_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003
 struct drm_msm_gem_submit_cmd {
-	uint32_t type;           /* in, one of MSM_SUBMIT_CMD_x */
-	uint32_t submit_idx;     /* in, index of submit_bo cmdstream buffer */
-	uint32_t submit_offset;  /* in, offset into submit_bo */
-	uint32_t size;           /* in, cmdstream size */
-	uint32_t pad;
-	uint32_t nr_relocs;      /* in, number of submit_reloc's */
-	uint64_t __user relocs;  /* in, ptr to array of submit_reloc's */
+	__u32 type;           /* in, one of MSM_SUBMIT_CMD_x */
+	__u32 submit_idx;     /* in, index of submit_bo cmdstream buffer */
+	__u32 submit_offset;  /* in, offset into submit_bo */
+	__u32 size;           /* in, cmdstream size */
+	__u32 pad;
+	__u32 nr_relocs;      /* in, number of submit_reloc's */
+	__u64 __user relocs;  /* in, ptr to array of submit_reloc's */
 };
 
 /* Each buffer referenced elsewhere in the cmdstream submit (ie. the
@@ -174,9 +178,9 @@ struct drm_msm_gem_submit_cmd {
 #define MSM_SUBMIT_BO_FLAGS            (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
 
 struct drm_msm_gem_submit_bo {
-	uint32_t flags;          /* in, mask of MSM_SUBMIT_BO_x */
-	uint32_t handle;         /* in, GEM handle */
-	uint64_t presumed;       /* in/out, presumed buffer address */
+	__u32 flags;          /* in, mask of MSM_SUBMIT_BO_x */
+	__u32 handle;         /* in, GEM handle */
+	__u64 presumed;       /* in/out, presumed buffer address */
 };
 
 /* Each cmdstream submit consists of a table of buffers involved, and
@@ -184,12 +188,12 @@ struct drm_msm_gem_submit_bo {
  * (context-restore), and IB buffers needed for per tile/bin draw cmds.
  */
 struct drm_msm_gem_submit {
-	uint32_t pipe;           /* in, MSM_PIPE_x */
-	uint32_t fence;          /* out */
-	uint32_t nr_bos;         /* in, number of submit_bo's */
-	uint32_t nr_cmds;        /* in, number of submit_cmd's */
-	uint64_t __user bos;     /* in, ptr to array of submit_bo's */
-	uint64_t __user cmds;    /* in, ptr to array of submit_cmd's */
+	__u32 pipe;           /* in, MSM_PIPE_x */
+	__u32 fence;          /* out */
+	__u32 nr_bos;         /* in, number of submit_bo's */
+	__u32 nr_cmds;        /* in, number of submit_cmd's */
+	__u64 __user bos;     /* in, ptr to array of submit_bo's */
+	__u64 __user cmds;    /* in, ptr to array of submit_cmd's */
 };
 
 /* The normal way to synchronize with the GPU is just to CPU_PREP on
@@ -200,11 +204,32 @@ struct drm_msm_gem_submit {
  * APIs without requiring a dummy bo to synchronize on.
  */
 struct drm_msm_wait_fence {
-	uint32_t fence;          /* in */
-	uint32_t pad;
+	__u32 fence;          /* in */
+	__u32 pad;
 	struct drm_msm_timespec timeout;   /* in */
 };
 
+/* madvise provides a way to tell the kernel in case a buffers contents
+ * can be discarded under memory pressure, which is useful for userspace
+ * bo cache where we want to optimistically hold on to buffer allocate
+ * and potential mmap, but allow the pages to be discarded under memory
+ * pressure.
+ *
+ * Typical usage would involve madvise(DONTNEED) when buffer enters BO
+ * cache, and madvise(WILLNEED) if trying to recycle buffer from BO cache.
+ * In the WILLNEED case, 'retained' indicates to userspace whether the
+ * backing pages still exist.
+ */
+#define MSM_MADV_WILLNEED 0       /* backing pages are needed, status returned in 'retained' */
+#define MSM_MADV_DONTNEED 1       /* backing pages not needed */
+#define __MSM_MADV_PURGED 2       /* internal state */
+
+struct drm_msm_gem_madvise {
+	__u32 handle;         /* in, GEM handle */
+	__u32 madv;           /* in, MSM_MADV_x */
+	__u32 retained;       /* out, whether backing store still exists */
+};
+
 #define DRM_MSM_GET_PARAM              0x00
 /* placeholder:
 #define DRM_MSM_SET_PARAM              0x01
@@ -215,7 +240,8 @@ struct drm_msm_wait_fence {
 #define DRM_MSM_GEM_CPU_FINI           0x05
 #define DRM_MSM_GEM_SUBMIT             0x06
 #define DRM_MSM_WAIT_FENCE             0x07
-#define DRM_MSM_NUM_IOCTLS             0x08
+#define DRM_MSM_GEM_MADVISE            0x08
+#define DRM_MSM_NUM_IOCTLS             0x09
 
 #define DRM_IOCTL_MSM_GET_PARAM        DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param)
 #define DRM_IOCTL_MSM_GEM_NEW          DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new)
@@ -224,5 +250,10 @@ struct drm_msm_wait_fence {
 #define DRM_IOCTL_MSM_GEM_CPU_FINI     DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_FINI, struct drm_msm_gem_cpu_fini)
 #define DRM_IOCTL_MSM_GEM_SUBMIT       DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_SUBMIT, struct drm_msm_gem_submit)
 #define DRM_IOCTL_MSM_WAIT_FENCE       DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_WAIT_FENCE, struct drm_msm_wait_fence)
+#define DRM_IOCTL_MSM_GEM_MADVISE      DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_MADVISE, struct drm_msm_gem_madvise)
+
+#if defined(__cplusplus)
+}
+#endif
 
 #endif /* __MSM_DRM_H__ */
commit 82780c87f9e39ab19dce6ec619ad744a26a0f886
Author: Rob Clark <[email protected]>
Date:   Tue May 31 10:46:59 2016 -0400

    freedreno: move bo-cache to it's own file
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/Makefile.sources b/freedreno/Makefile.sources
index 57a8bf1..68a679b 100644
--- a/freedreno/Makefile.sources
+++ b/freedreno/Makefile.sources
@@ -4,6 +4,7 @@ LIBDRM_FREEDRENO_FILES := \
 	freedreno_priv.h \
 	freedreno_ringbuffer.c \
 	freedreno_bo.c \
+	freedreno_bo_cache.c \
 	msm/msm_bo.c \
 	msm/msm_device.c \
 	msm/msm_drm.h \
diff --git a/freedreno/freedreno_bo.c b/freedreno/freedreno_bo.c
index da56398..cf2d7cb 100644
--- a/freedreno/freedreno_bo.c
+++ b/freedreno/freedreno_bo.c
@@ -33,9 +33,8 @@
 #include "freedreno_drmif.h"
 #include "freedreno_priv.h"
 
-static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
-
-static void bo_del(struct fd_bo *bo);
+drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+drm_private void bo_del(struct fd_bo *bo);
 
 /* set buffer name, and add to table, call w/ table_lock held: */
 static void set_name(struct fd_bo *bo, uint32_t name)
@@ -83,116 +82,6 @@ static struct fd_bo * bo_from_handle(struct fd_device *dev,
 	return bo;
 }
 
-/* Frees older cached buffers.  Called under table_lock */
-drm_private void
-fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time)
-{
-	int i;
-
-	if (cache->time == time)
-		return;
-
-	for (i = 0; i < cache->num_buckets; i++) {
-		struct fd_bo_bucket *bucket = &cache->cache_bucket[i];
-		struct fd_bo *bo;
-
-		while (!LIST_IS_EMPTY(&bucket->list)) {
-			bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list);
-
-			/* keep things in cache for at least 1 second: */
-			if (time && ((time - bo->free_time) <= 1))
-				break;
-
-			list_del(&bo->list);
-			bo_del(bo);
-		}
-	}
-
-	cache->time = time;
-}
-
-static struct fd_bo_bucket * get_bucket(struct fd_bo_cache *cache, uint32_t size)
-{
-	int i;
-
-	/* hmm, this is what intel does, but I suppose we could calculate our
-	 * way to the correct bucket size rather than looping..
-	 */
-	for (i = 0; i < cache->num_buckets; i++) {
-		struct fd_bo_bucket *bucket = &cache->cache_bucket[i];
-		if (bucket->size >= size) {
-			return bucket;
-		}
-	}
-
-	return NULL;
-}
-
-static int is_idle(struct fd_bo *bo)
-{
-	return fd_bo_cpu_prep(bo, NULL,
-			DRM_FREEDRENO_PREP_READ |
-			DRM_FREEDRENO_PREP_WRITE |
-			DRM_FREEDRENO_PREP_NOSYNC) == 0;
-}
-
-static struct fd_bo *find_in_bucket(struct fd_bo_bucket *bucket, uint32_t flags)
-{
-	struct fd_bo *bo = NULL;
-
-	/* TODO .. if we had an ALLOC_FOR_RENDER flag like intel, we could
-	 * skip the busy check.. if it is only going to be a render target
-	 * then we probably don't need to stall..
-	 *
-	 * NOTE that intel takes ALLOC_FOR_RENDER bo's from the list tail
-	 * (MRU, since likely to be in GPU cache), rather than head (LRU)..
-	 */
-	pthread_mutex_lock(&table_lock);
-	while (!LIST_IS_EMPTY(&bucket->list)) {
-		bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list);
-		if (0 /* TODO: if madvise tells us bo is gone... */) {
-			list_del(&bo->list);
-			bo_del(bo);
-			bo = NULL;
-			continue;
-		}
-		/* TODO check for compatible flags? */
-		if (is_idle(bo)) {
-			list_del(&bo->list);
-			break;
-		}
-		bo = NULL;
-		break;
-	}
-	pthread_mutex_unlock(&table_lock);
-
-	return bo;
-}
-
-/* NOTE: size is potentially rounded up to bucket size: */
-drm_private struct fd_bo *
-fd_bo_cache_alloc(struct fd_bo_cache *cache, uint32_t *size, uint32_t flags)
-{
-	struct fd_bo *bo = NULL;
-	struct fd_bo_bucket *bucket;
-
-	*size = ALIGN(*size, 4096);
-	bucket = get_bucket(cache, *size);
-
-	/* see if we can be green and recycle: */
-	if (bucket) {
-		*size = bucket->size;
-		bo = find_in_bucket(bucket, flags);
-		if (bo) {
-			atomic_set(&bo->refcnt, 1);
-			fd_device_ref(bo->dev);
-			return bo;
-		}
-	}
-
-	return NULL;
-}
-
 struct fd_bo *
 fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
 {
@@ -303,32 +192,6 @@ struct fd_bo * fd_bo_ref(struct fd_bo *bo)
 	return bo;
 }
 
-drm_private int
-fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo)
-{
-	struct fd_bo_bucket *bucket = get_bucket(cache, bo->size);
-
-	/* see if we can be green and recycle: */
-	if (bucket) {
-		struct timespec time;
-
-		clock_gettime(CLOCK_MONOTONIC, &time);
-
-		bo->free_time = time.tv_sec;
-		list_addtail(&bo->list, &bucket->list);
-		fd_bo_cache_cleanup(cache, time.tv_sec);
-
-		/* bo's in the bucket cache don't have a ref and
-		 * don't hold a ref to the dev:
-		 */
-		fd_device_del_locked(bo->dev);
-
-		return 0;
-	}
-
-	return -1;
-}
-
 void fd_bo_del(struct fd_bo *bo)
 {
 	struct fd_device *dev = bo->dev;
@@ -348,7 +211,7 @@ out:
 }
 
 /* Called under table_lock */
-static void bo_del(struct fd_bo *bo)
+drm_private void bo_del(struct fd_bo *bo)
 {
 	if (bo->map)
 		drm_munmap(bo->map, bo->size);
diff --git a/freedreno/freedreno_bo_cache.c b/freedreno/freedreno_bo_cache.c
new file mode 100644
index 0000000..17199d2
--- /dev/null
+++ b/freedreno/freedreno_bo_cache.c
@@ -0,0 +1,205 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2016 Rob Clark <[email protected]>
+ *
+ * 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 (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 NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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:
+ *    Rob Clark <[email protected]>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+
+
+drm_private void bo_del(struct fd_bo *bo);
+drm_private extern pthread_mutex_t table_lock;
+
+static void
+add_bucket(struct fd_bo_cache *cache, int size)
+{
+	unsigned int i = cache->num_buckets;
+
+	assert(i < ARRAY_SIZE(cache->cache_bucket));
+
+	list_inithead(&cache->cache_bucket[i].list);
+	cache->cache_bucket[i].size = size;
+	cache->num_buckets++;
+}
+
+drm_private void
+fd_bo_cache_init(struct fd_bo_cache *cache)
+{
+	unsigned long size, cache_max_size = 64 * 1024 * 1024;
+
+	/* OK, so power of two buckets was too wasteful of memory.
+	 * Give 3 other sizes between each power of two, to hopefully
+	 * cover things accurately enough.  (The alternative is
+	 * probably to just go for exact matching of sizes, and assume
+	 * that for things like composited window resize the tiled
+	 * width/height alignment and rounding of sizes to pages will
+	 * get us useful cache hit rates anyway)
+	 */
+	add_bucket(cache, 4096);
+	add_bucket(cache, 4096 * 2);
+	add_bucket(cache, 4096 * 3);
+
+	/* Initialize the linked lists for BO reuse cache. */
+	for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
+		add_bucket(cache, size);
+		add_bucket(cache, size + size * 1 / 4);
+		add_bucket(cache, size + size * 2 / 4);
+		add_bucket(cache, size + size * 3 / 4);
+	}
+}
+
+/* Frees older cached buffers.  Called under table_lock */
+drm_private void
+fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time)
+{
+	int i;
+
+	if (cache->time == time)
+		return;
+
+	for (i = 0; i < cache->num_buckets; i++) {
+		struct fd_bo_bucket *bucket = &cache->cache_bucket[i];
+		struct fd_bo *bo;
+
+		while (!LIST_IS_EMPTY(&bucket->list)) {
+			bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list);
+
+			/* keep things in cache for at least 1 second: */
+			if (time && ((time - bo->free_time) <= 1))
+				break;
+
+			list_del(&bo->list);
+			bo_del(bo);
+		}
+	}
+
+	cache->time = time;
+}
+
+static struct fd_bo_bucket * get_bucket(struct fd_bo_cache *cache, uint32_t size)
+{
+	int i;
+
+	/* hmm, this is what intel does, but I suppose we could calculate our
+	 * way to the correct bucket size rather than looping..
+	 */
+	for (i = 0; i < cache->num_buckets; i++) {
+		struct fd_bo_bucket *bucket = &cache->cache_bucket[i];
+		if (bucket->size >= size) {
+			return bucket;
+		}
+	}
+
+	return NULL;
+}
+
+static int is_idle(struct fd_bo *bo)
+{
+	return fd_bo_cpu_prep(bo, NULL,
+			DRM_FREEDRENO_PREP_READ |
+			DRM_FREEDRENO_PREP_WRITE |
+			DRM_FREEDRENO_PREP_NOSYNC) == 0;
+}
+
+static struct fd_bo *find_in_bucket(struct fd_bo_bucket *bucket, uint32_t flags)
+{
+	struct fd_bo *bo = NULL;
+
+	/* TODO .. if we had an ALLOC_FOR_RENDER flag like intel, we could
+	 * skip the busy check.. if it is only going to be a render target
+	 * then we probably don't need to stall..
+	 *
+	 * NOTE that intel takes ALLOC_FOR_RENDER bo's from the list tail
+	 * (MRU, since likely to be in GPU cache), rather than head (LRU)..
+	 */
+	pthread_mutex_lock(&table_lock);
+	if (!LIST_IS_EMPTY(&bucket->list)) {
+		bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list);
+		/* TODO check for compatible flags? */
+		if (is_idle(bo)) {
+			list_del(&bo->list);
+		} else {
+			bo = NULL;
+		}
+	}
+	pthread_mutex_unlock(&table_lock);
+
+	return bo;
+}
+
+/* NOTE: size is potentially rounded up to bucket size: */
+drm_private struct fd_bo *
+fd_bo_cache_alloc(struct fd_bo_cache *cache, uint32_t *size, uint32_t flags)
+{
+	struct fd_bo *bo = NULL;
+	struct fd_bo_bucket *bucket;
+
+	*size = ALIGN(*size, 4096);
+	bucket = get_bucket(cache, *size);
+
+	/* see if we can be green and recycle: */
+	if (bucket) {
+		*size = bucket->size;
+		bo = find_in_bucket(bucket, flags);
+		if (bo) {
+			atomic_set(&bo->refcnt, 1);
+			fd_device_ref(bo->dev);
+			return bo;
+		}
+	}
+
+	return NULL;
+}
+
+drm_private int
+fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo)
+{
+	struct fd_bo_bucket *bucket = get_bucket(cache, bo->size);
+
+	/* see if we can be green and recycle: */
+	if (bucket) {
+		struct timespec time;
+
+		clock_gettime(CLOCK_MONOTONIC, &time);
+
+		bo->free_time = time.tv_sec;
+		list_addtail(&bo->list, &bucket->list);
+		fd_bo_cache_cleanup(cache, time.tv_sec);
+
+		/* bo's in the bucket cache don't have a ref and
+		 * don't hold a ref to the dev:
+		 */
+		fd_device_del_locked(bo->dev);
+
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c
index bd57c24..15e41f0 100644
--- a/freedreno/freedreno_device.c
+++ b/freedreno/freedreno_device.c
@@ -42,44 +42,6 @@ static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
 struct fd_device * kgsl_device_new(int fd);
 struct fd_device * msm_device_new(int fd);
 
-static void
-add_bucket(struct fd_bo_cache *cache, int size)
-{
-	unsigned int i = cache->num_buckets;
-
-	assert(i < ARRAY_SIZE(cache->cache_bucket));
-
-	list_inithead(&cache->cache_bucket[i].list);
-	cache->cache_bucket[i].size = size;
-	cache->num_buckets++;
-}
-
-drm_private void
-fd_bo_cache_init(struct fd_bo_cache *cache)
-{
-	unsigned long size, cache_max_size = 64 * 1024 * 1024;
-
-	/* OK, so power of two buckets was too wasteful of memory.
-	 * Give 3 other sizes between each power of two, to hopefully
-	 * cover things accurately enough.  (The alternative is
-	 * probably to just go for exact matching of sizes, and assume
-	 * that for things like composited window resize the tiled
-	 * width/height alignment and rounding of sizes to pages will
-	 * get us useful cache hit rates anyway)
-	 */
-	add_bucket(cache, 4096);
-	add_bucket(cache, 4096 * 2);
-	add_bucket(cache, 4096 * 3);
-
-	/* Initialize the linked lists for BO reuse cache. */
-	for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
-		add_bucket(cache, size);
-		add_bucket(cache, size + size * 1 / 4);
-		add_bucket(cache, size + size * 2 / 4);
-		add_bucket(cache, size + size * 3 / 4);
-	}
-}
-
 struct fd_device * fd_device_new(int fd)
 {
 	struct fd_device *dev;
commit 0b34b683071901e1ffa82a2762e71c185217c1bc
Author: Rob Clark <[email protected]>
Date:   Mon May 30 12:45:33 2016 -0400

    freedreno: refactor bo-cache API
    
    Split out interface to allocate from and release to bo-cache, and get
    rid of direct usage of bucket level API from fd_bo/etc.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_bo.c b/freedreno/freedreno_bo.c
index 7b3e51f..da56398 100644
--- a/freedreno/freedreno_bo.c
+++ b/freedreno/freedreno_bo.c
@@ -84,7 +84,8 @@ static struct fd_bo * bo_from_handle(struct fd_device *dev,
 }
 
 /* Frees older cached buffers.  Called under table_lock */
-drm_private void fd_cleanup_bo_cache(struct fd_bo_cache *cache, time_t time)
+drm_private void
+fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time)
 {
 	int i;
 
@@ -168,21 +169,19 @@ static struct fd_bo *find_in_bucket(struct fd_bo_bucket *bucket, uint32_t flags)
 	return bo;
 }
 
-
-struct fd_bo *
-fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
+/* NOTE: size is potentially rounded up to bucket size: */
+drm_private struct fd_bo *
+fd_bo_cache_alloc(struct fd_bo_cache *cache, uint32_t *size, uint32_t flags)
 {
 	struct fd_bo *bo = NULL;
 	struct fd_bo_bucket *bucket;
-	uint32_t handle;
-	int ret;
 
-	size = ALIGN(size, 4096);
-	bucket = get_bucket(&dev->bo_cache, size);
+	*size = ALIGN(*size, 4096);
+	bucket = get_bucket(cache, *size);
 
 	/* see if we can be green and recycle: */
 	if (bucket) {
-		size = bucket->size;
+		*size = bucket->size;
 		bo = find_in_bucket(bucket, flags);
 		if (bo) {
 			atomic_set(&bo->refcnt, 1);
@@ -191,6 +190,20 @@ fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
 		}
 	}
 
+	return NULL;
+}
+
+struct fd_bo *
+fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
+{
+	struct fd_bo *bo = NULL;
+	uint32_t handle;
+	int ret;
+
+	bo = fd_bo_cache_alloc(&dev->bo_cache, &size, flags);
+	if (bo)
+		return bo;
+
 	ret = dev->funcs->bo_new_handle(dev, size, flags, &handle);
 	if (ret)
 		return NULL;
@@ -290,39 +303,47 @@ struct fd_bo * fd_bo_ref(struct fd_bo *bo)
 	return bo;
 }
 
-void fd_bo_del(struct fd_bo *bo)
+drm_private int
+fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo)
 {
-	struct fd_device *dev = bo->dev;
+	struct fd_bo_bucket *bucket = get_bucket(cache, bo->size);
 
-	if (!atomic_dec_and_test(&bo->refcnt))
-		return;
+	/* see if we can be green and recycle: */
+	if (bucket) {
+		struct timespec time;
 
-	pthread_mutex_lock(&table_lock);
+		clock_gettime(CLOCK_MONOTONIC, &time);
 
-	if (bo->bo_reuse) {
-		struct fd_bo_bucket *bucket = get_bucket(&dev->bo_cache, bo->size);
+		bo->free_time = time.tv_sec;
+		list_addtail(&bo->list, &bucket->list);
+		fd_bo_cache_cleanup(cache, time.tv_sec);
 
-		/* see if we can be green and recycle: */
-		if (bucket) {
-			struct timespec time;
+		/* bo's in the bucket cache don't have a ref and
+		 * don't hold a ref to the dev:
+		 */
+		fd_device_del_locked(bo->dev);
 
-			clock_gettime(CLOCK_MONOTONIC, &time);
+		return 0;
+	}
 
-			bo->free_time = time.tv_sec;
-			list_addtail(&bo->list, &bucket->list);
-			fd_cleanup_bo_cache(&dev->bo_cache, time.tv_sec);
+	return -1;
+}
 
-			/* bo's in the bucket cache don't have a ref and
-			 * don't hold a ref to the dev:
-			 */
+void fd_bo_del(struct fd_bo *bo)
+{
+	struct fd_device *dev = bo->dev;
 
-			goto out;
-		}
-	}
+	if (!atomic_dec_and_test(&bo->refcnt))
+		return;
+
+	pthread_mutex_lock(&table_lock);
+
+	if (bo->bo_reuse && (fd_bo_cache_free(&dev->bo_cache, bo) == 0))
+		goto out;
 
 	bo_del(bo);
-out:
 	fd_device_del_locked(dev);
+out:
 	pthread_mutex_unlock(&table_lock);
 }
 
diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c
index 0e20332..bd57c24 100644
--- a/freedreno/freedreno_device.c
+++ b/freedreno/freedreno_device.c
@@ -54,7 +54,7 @@ add_bucket(struct fd_bo_cache *cache, int size)
 	cache->num_buckets++;
 }
 
-static void
+drm_private void
 fd_bo_cache_init(struct fd_bo_cache *cache)
 {
 	unsigned long size, cache_max_size = 64 * 1024 * 1024;
@@ -137,7 +137,7 @@ struct fd_device * fd_device_ref(struct fd_device *dev)
 
 static void fd_device_del_impl(struct fd_device *dev)
 {
-	fd_cleanup_bo_cache(&dev->bo_cache, 0);
+	fd_bo_cache_cleanup(&dev->bo_cache, 0);
 	drmHashDestroy(dev->handle_table);
 	drmHashDestroy(dev->name_table);
 	if (dev->closefd)
diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h
index 5880dc2..4159e52 100644
--- a/freedreno/freedreno_priv.h
+++ b/freedreno/freedreno_priv.h
@@ -96,7 +96,11 @@ struct fd_device {
 	int closefd;        /* call close(fd) upon destruction */
 };
 
-drm_private void fd_cleanup_bo_cache(struct fd_bo_cache *cache, time_t time);
+drm_private void fd_bo_cache_init(struct fd_bo_cache *cache);
+drm_private void fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time);
+drm_private struct fd_bo * fd_bo_cache_alloc(struct fd_bo_cache *cache,
+		uint32_t *size, uint32_t flags);
+drm_private int fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo);
 
 /* for where @table_lock is already held: */
 drm_private void fd_device_del_locked(struct fd_device *dev);
commit b18b6e21fc4ae488f61492f46d8e6705e106593b
Author: Rob Clark <[email protected]>
Date:   Mon May 30 11:49:39 2016 -0400

    freedreno: split out fd_bo_cache
    
    Eventually we'll want a separate bo-cache for ringbuffer bo's, since
    ringbuffer bo's get vmap'd on the kernel side, it is preferrable to
    re-use them as ringbuffers rather than something else.  Plus should
    help to add madvise support if it is a bit better decoupled from bo
    allocation (next patch).
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_bo.c b/freedreno/freedreno_bo.c
index 82c1f15..7b3e51f 100644
--- a/freedreno/freedreno_bo.c
+++ b/freedreno/freedreno_bo.c
@@ -84,15 +84,15 @@ static struct fd_bo * bo_from_handle(struct fd_device *dev,
 }
 
 /* Frees older cached buffers.  Called under table_lock */
-drm_private void fd_cleanup_bo_cache(struct fd_device *dev, time_t time)
+drm_private void fd_cleanup_bo_cache(struct fd_bo_cache *cache, time_t time)
 {
 	int i;
 
-	if (dev->time == time)
+	if (cache->time == time)
 		return;
 
-	for (i = 0; i < dev->num_buckets; i++) {
-		struct fd_bo_bucket *bucket = &dev->cache_bucket[i];
+	for (i = 0; i < cache->num_buckets; i++) {
+		struct fd_bo_bucket *bucket = &cache->cache_bucket[i];
 		struct fd_bo *bo;
 
 		while (!LIST_IS_EMPTY(&bucket->list)) {
@@ -107,18 +107,18 @@ drm_private void fd_cleanup_bo_cache(struct fd_device *dev, time_t time)
 		}
 	}
 
-	dev->time = time;
+	cache->time = time;
 }
 
-static struct fd_bo_bucket * get_bucket(struct fd_device *dev, uint32_t size)
+static struct fd_bo_bucket * get_bucket(struct fd_bo_cache *cache, uint32_t size)
 {
 	int i;
 
 	/* hmm, this is what intel does, but I suppose we could calculate our
 	 * way to the correct bucket size rather than looping..
 	 */
-	for (i = 0; i < dev->num_buckets; i++) {
-		struct fd_bo_bucket *bucket = &dev->cache_bucket[i];
+	for (i = 0; i < cache->num_buckets; i++) {
+		struct fd_bo_bucket *bucket = &cache->cache_bucket[i];
 		if (bucket->size >= size) {
 			return bucket;
 		}
@@ -135,8 +135,7 @@ static int is_idle(struct fd_bo *bo)
 			DRM_FREEDRENO_PREP_NOSYNC) == 0;
 }
 
-static struct fd_bo *find_in_bucket(struct fd_device *dev,
-		struct fd_bo_bucket *bucket, uint32_t flags)
+static struct fd_bo *find_in_bucket(struct fd_bo_bucket *bucket, uint32_t flags)
 {
 	struct fd_bo *bo = NULL;
 
@@ -179,12 +178,12 @@ fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
 	int ret;
 
 	size = ALIGN(size, 4096);
-	bucket = get_bucket(dev, size);
+	bucket = get_bucket(&dev->bo_cache, size);
 
 	/* see if we can be green and recycle: */
 	if (bucket) {
 		size = bucket->size;
-		bo = find_in_bucket(dev, bucket, flags);
+		bo = find_in_bucket(bucket, flags);
 		if (bo) {
 			atomic_set(&bo->refcnt, 1);
 			fd_device_ref(bo->dev);
@@ -301,7 +300,7 @@ void fd_bo_del(struct fd_bo *bo)
 	pthread_mutex_lock(&table_lock);
 
 	if (bo->bo_reuse) {
-		struct fd_bo_bucket *bucket = get_bucket(dev, bo->size);
+		struct fd_bo_bucket *bucket = get_bucket(&dev->bo_cache, bo->size);
 
 		/* see if we can be green and recycle: */
 		if (bucket) {
@@ -311,7 +310,7 @@ void fd_bo_del(struct fd_bo *bo)
 
 			bo->free_time = time.tv_sec;
 			list_addtail(&bo->list, &bucket->list);
-			fd_cleanup_bo_cache(dev, time.tv_sec);
+			fd_cleanup_bo_cache(&dev->bo_cache, time.tv_sec);
 
 			/* bo's in the bucket cache don't have a ref and
 			 * don't hold a ref to the dev:
diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c
index ddb9545..0e20332 100644
--- a/freedreno/freedreno_device.c
+++ b/freedreno/freedreno_device.c
@@ -43,19 +43,19 @@ struct fd_device * kgsl_device_new(int fd);
 struct fd_device * msm_device_new(int fd);
 
 static void
-add_bucket(struct fd_device *dev, int size)
+add_bucket(struct fd_bo_cache *cache, int size)
 {
-	unsigned int i = dev->num_buckets;
+	unsigned int i = cache->num_buckets;
 
-	assert(i < ARRAY_SIZE(dev->cache_bucket));
+	assert(i < ARRAY_SIZE(cache->cache_bucket));
 
-	list_inithead(&dev->cache_bucket[i].list);
-	dev->cache_bucket[i].size = size;
-	dev->num_buckets++;
+	list_inithead(&cache->cache_bucket[i].list);
+	cache->cache_bucket[i].size = size;
+	cache->num_buckets++;
 }
 
 static void
-init_cache_buckets(struct fd_device *dev)
+fd_bo_cache_init(struct fd_bo_cache *cache)
 {
 	unsigned long size, cache_max_size = 64 * 1024 * 1024;
 
@@ -67,16 +67,16 @@ init_cache_buckets(struct fd_device *dev)
 	 * width/height alignment and rounding of sizes to pages will
 	 * get us useful cache hit rates anyway)
 	 */
-	add_bucket(dev, 4096);
-	add_bucket(dev, 4096 * 2);
-	add_bucket(dev, 4096 * 3);
+	add_bucket(cache, 4096);
+	add_bucket(cache, 4096 * 2);
+	add_bucket(cache, 4096 * 3);
 
 	/* Initialize the linked lists for BO reuse cache. */
 	for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
-		add_bucket(dev, size);
-		add_bucket(dev, size + size * 1 / 4);
-		add_bucket(dev, size + size * 2 / 4);
-		add_bucket(dev, size + size * 3 / 4);
+		add_bucket(cache, size);
+		add_bucket(cache, size + size * 1 / 4);
+		add_bucket(cache, size + size * 2 / 4);
+		add_bucket(cache, size + size * 3 / 4);
 	}
 }
 
@@ -113,7 +113,7 @@ struct fd_device * fd_device_new(int fd)
 	dev->fd = fd;
 	dev->handle_table = drmHashCreate();
 	dev->name_table = drmHashCreate();
-	init_cache_buckets(dev);
+	fd_bo_cache_init(&dev->bo_cache);
 
 	return dev;
 }
@@ -137,7 +137,7 @@ struct fd_device * fd_device_ref(struct fd_device *dev)
 
 static void fd_device_del_impl(struct fd_device *dev)
 {
-	fd_cleanup_bo_cache(dev, 0);
+	fd_cleanup_bo_cache(&dev->bo_cache, 0);
 	drmHashDestroy(dev->handle_table);
 	drmHashDestroy(dev->name_table);
 	if (dev->closefd)
diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h
index 835fadb..5880dc2 100644
--- a/freedreno/freedreno_priv.h
+++ b/freedreno/freedreno_priv.h
@@ -68,6 +68,12 @@ struct fd_bo_bucket {
 	struct list_head list;
 };
 
+struct fd_bo_cache {
+	struct fd_bo_bucket cache_bucket[14 * 4];
+	int num_buckets;
+	time_t time;
+};
+
 struct fd_device {
 	int fd;
 	atomic_t refcnt;
@@ -85,14 +91,12 @@ struct fd_device {
 
 	const struct fd_device_funcs *funcs;
 
-	struct fd_bo_bucket cache_bucket[14 * 4];
-	int num_buckets;
-	time_t time;
+	struct fd_bo_cache bo_cache;
 
 	int closefd;        /* call close(fd) upon destruction */
 };
 
-drm_private void fd_cleanup_bo_cache(struct fd_device *dev, time_t time);
+drm_private void fd_cleanup_bo_cache(struct fd_bo_cache *cache, time_t time);
 
 /* for where @table_lock is already held: */
 drm_private void fd_device_del_locked(struct fd_device *dev);
commit 2ca73c666aca726d9f6b6ddc2b5ee1d28513320f
Author: Rob Clark <[email protected]>
Date:   Fri May 20 17:19:04 2016 -0400

    freedreno: add simpler ring-reloc
    
    Provide a way to insert a reference (ie. OUT_IB()) to a target ring,
    executing all the cmds in the target ring from the start.
    
    Sometimes the ringmarker stuff is just overkill.  And it will won't
    really work properly once we support multiple physical cmdstream buffers
    per fd_ringbuffer.  So in the future the old ringmarker related APIs
    will be deprecated in a few releases.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_drmif.h b/freedreno/freedreno_drmif.h
index 02dcfd2..15ae075 100644
--- a/freedreno/freedreno_drmif.h
+++ b/freedreno/freedreno_drmif.h
@@ -32,6 +32,15 @@
 #include <xf86drm.h>
 #include <stdint.h>
 
+#if defined(__GNUC__)
+#  define deprecated __attribute__((__deprecated__))
+#else
+#  define deprecated
+#endif
+
+/* an empty marker for things that will be deprecated in the future: */
+#define will_be_deprecated
+
 struct fd_bo;
 struct fd_pipe;
 struct fd_device;
diff --git a/freedreno/freedreno_ringbuffer.c b/freedreno/freedreno_ringbuffer.c
index 9d987aa..be80296 100644
--- a/freedreno/freedreno_ringbuffer.c
+++ b/freedreno/freedreno_ringbuffer.c
@@ -112,6 +112,16 @@ fd_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
 	ring->funcs->emit_reloc_ring(ring, target->ring, submit_offset, size);
 }
 
+uint32_t
+fd_ringbuffer_emit_reloc_ring_full(struct fd_ringbuffer *ring,
+		struct fd_ringbuffer *target, uint32_t cmd_idx)
+{
+	uint32_t size = offset_bytes(target->cur, target->start);
+	assert(cmd_idx == 0);
+	ring->funcs->emit_reloc_ring(ring, target, 0, size);
+	return size;
+}
+
 struct fd_ringmarker * fd_ringmarker_new(struct fd_ringbuffer *ring)
 {
 	struct fd_ringmarker *marker = NULL;
@@ -124,7 +134,7 @@ struct fd_ringmarker * fd_ringmarker_new(struct fd_ringbuffer *ring)
 
 	marker->ring = ring;
 
-	fd_ringmarker_mark(marker);
+	marker->cur = marker->ring->cur;
 
 	return marker;
 }
diff --git a/freedreno/freedreno_ringbuffer.h b/freedreno/freedreno_ringbuffer.h
index 578cdb2..643f50b 100644
--- a/freedreno/freedreno_ringbuffer.h
+++ b/freedreno/freedreno_ringbuffer.h
@@ -75,14 +75,16 @@ struct fd_reloc {
 };
 
 void fd_ringbuffer_reloc(struct fd_ringbuffer *ring, const struct fd_reloc *reloc);
-void fd_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
+will_be_deprecated void fd_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
 		struct fd_ringmarker *target, struct fd_ringmarker *end);
+uint32_t fd_ringbuffer_emit_reloc_ring_full(struct fd_ringbuffer *ring,
+		struct fd_ringbuffer *target, uint32_t cmd_idx);
 
-struct fd_ringmarker * fd_ringmarker_new(struct fd_ringbuffer *ring);
-void fd_ringmarker_del(struct fd_ringmarker *marker);
-void fd_ringmarker_mark(struct fd_ringmarker *marker);
-uint32_t fd_ringmarker_dwords(struct fd_ringmarker *start,
+will_be_deprecated struct fd_ringmarker * fd_ringmarker_new(struct fd_ringbuffer *ring);
+will_be_deprecated void fd_ringmarker_del(struct fd_ringmarker *marker);
+will_be_deprecated void fd_ringmarker_mark(struct fd_ringmarker *marker);
+will_be_deprecated uint32_t fd_ringmarker_dwords(struct fd_ringmarker *start,
 		struct fd_ringmarker *end);
-int fd_ringmarker_flush(struct fd_ringmarker *marker);
+will_be_deprecated int fd_ringmarker_flush(struct fd_ringmarker *marker);
 
 #endif /* FREEDRENO_RINGBUFFER_H_ */
commit 73db0a0421006d7b4af837c7a77dacbb005b800f
Author: Rob Clark <[email protected]>
Date:   Fri May 20 17:14:43 2016 -0400

    freedreno: rework internal ring->emit_reloc_ring()
    
    No need for it to deal with ringmarkers.
    
    Signed-off-by: Rob Clark <[email protected]>

diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h
index 53817b1..835fadb 100644
--- a/freedreno/freedreno_priv.h
+++ b/freedreno/freedreno_priv.h
@@ -122,7 +122,8 @@ struct fd_ringbuffer_funcs {
 	void (*emit_reloc)(struct fd_ringbuffer *ring,
 			const struct fd_reloc *reloc);
 	void (*emit_reloc_ring)(struct fd_ringbuffer *ring,
-			struct fd_ringmarker *target, struct fd_ringmarker *end);
+			struct fd_ringbuffer *target,
+			uint32_t submit_offset, uint32_t size);
 	void (*destroy)(struct fd_ringbuffer *ring);
 };
 
@@ -168,4 +169,10 @@ struct fd_bo {
 #define U642VOID(x) ((void *)(unsigned long)(x))
 #define VOID2U64(x) ((uint64_t)(unsigned long)(x))
 
+static inline uint32_t
+offset_bytes(void *end, void *start)
+{
+	return ((char *)end) - ((char *)start);
+}
+
 #endif /* FREEDRENO_PRIV_H_ */
diff --git a/freedreno/freedreno_ringbuffer.c b/freedreno/freedreno_ringbuffer.c
index 984da24..9d987aa 100644
--- a/freedreno/freedreno_ringbuffer.c
+++ b/freedreno/freedreno_ringbuffer.c
@@ -102,8 +102,14 @@ fd_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
 			      struct fd_ringmarker *target,
 			      struct fd_ringmarker *end)
 {
+	uint32_t submit_offset, size;
+
 	assert(target->ring == end->ring);
-	ring->funcs->emit_reloc_ring(ring, target, end);
+
+	submit_offset = offset_bytes(target->cur, target->ring->start);
+	size = offset_bytes(end->cur, target->cur);
+
+	ring->funcs->emit_reloc_ring(ring, target->ring, submit_offset, size);
 }
 
 struct fd_ringmarker * fd_ringmarker_new(struct fd_ringbuffer *ring)
diff --git a/freedreno/kgsl/kgsl_ringbuffer.c b/freedreno/kgsl/kgsl_ringbuffer.c
index 6f68f2f..a0bc9d0 100644
--- a/freedreno/kgsl/kgsl_ringbuffer.c
+++ b/freedreno/kgsl/kgsl_ringbuffer.c
@@ -174,11 +174,11 @@ static void kgsl_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
 }
 
 static void kgsl_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
-		struct fd_ringmarker *target, struct fd_ringmarker *end)
+		struct fd_ringbuffer *target,
+		uint32_t submit_offset, uint32_t size)
 {
-	struct kgsl_ringbuffer *target_ring = to_kgsl_ringbuffer(target->ring);
-	(*ring->cur++) = target_ring->bo->gpuaddr +
-			(uint8_t *)target->cur - (uint8_t *)target->ring->start;
+	struct kgsl_ringbuffer *target_ring = to_kgsl_ringbuffer(target);
+	(*ring->cur++) = target_ring->bo->gpuaddr + submit_offset;
 }
 
 static void kgsl_ringbuffer_destroy(struct fd_ringbuffer *ring)
diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c
index 34bc2fe..b5a50de 100644
--- a/freedreno/msm/msm_ringbuffer.c
+++ b/freedreno/msm/msm_ringbuffer.c
@@ -143,11 +143,6 @@ static int check_cmd_bo(struct fd_ringbuffer *ring,
 	return msm_ring->submit.bos[cmd->submit_idx].handle == bo->handle;
 }
 
-static uint32_t offset_bytes(void *end, void *start)
-{
-	return ((char *)end) - ((char *)start);
-}
-
 static struct drm_msm_gem_submit_cmd * get_cmd(struct fd_ringbuffer *ring,
 		struct fd_ringbuffer *target_ring, struct fd_bo *target_bo,
 		uint32_t submit_offset, uint32_t size, uint32_t type)
@@ -328,16 +323,13 @@ static void msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
 }
 
 static void msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
-		struct fd_ringmarker *target, struct fd_ringmarker *end)
+		struct fd_ringbuffer *target,
+		uint32_t submit_offset, uint32_t size)
 {
-	struct fd_bo *target_bo = to_msm_ringbuffer(target->ring)->ring_bo;
+	struct fd_bo *target_bo = to_msm_ringbuffer(target)->ring_bo;
 	struct drm_msm_gem_submit_cmd *cmd;
-	uint32_t submit_offset, size;
-
-	submit_offset = offset_bytes(target->cur, target->ring->start);
-	size = offset_bytes(end->cur, target->cur);
 
-	cmd = get_cmd(ring, target->ring, target_bo, submit_offset, size,
+	cmd = get_cmd(ring, target, target_bo, submit_offset, size,
 			MSM_SUBMIT_CMD_IB_TARGET_BUF);
 	assert(cmd);
 

------------------------------------------------------------------------------
What NetFlow Analyzer can do for you? Monitors network bandwidth and traffic
patterns at an interface-level. Reveals which users, apps, and protocols are 
consuming the most bandwidth. Provides multi-vendor support for NetFlow, 
J-Flow, sFlow and other flows. Make informed decisions using capacity planning
reports.http://sdm.link/zohodev2dev
--
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.