+/** Helper to push a chunk into the state table.
+ *
+ * It creates 'count' entries into the state table and update their sizes,
+ * offsets and maps, also pushing them as "free" states.
+ */
+static void
+anv_state_pool_return_blocks(struct anv_state_pool *pool,
+ uint32_t chunk_offset, uint32_t count,
+ uint32_t block_size)
+{
+ /* Disallow returning 0 chunks */
+ assert(count != 0);
+
+ /* Make sure we always return chunks aligned to the block_size */
+ assert(chunk_offset % block_size == 0);
+
+ uint32_t st_idx;
+ UNUSED VkResult result = anv_state_table_add(&pool->table, &st_idx, count);
+ assert(result == VK_SUCCESS);
+ for (int i = 0; i < count; i++) {
+ /* update states that were added back to the state table */
+ struct anv_state *state_i = anv_state_table_get(&pool->table,
+ st_idx + i);
+ state_i->alloc_size = block_size;
+ state_i->offset = pool->start_offset + chunk_offset + block_size * i;
+ state_i->map = anv_block_pool_map(&pool->block_pool,
+ state_i->offset,
+ state_i->alloc_size);
+ }
+
+ uint32_t block_bucket = anv_state_pool_get_bucket(block_size);
+ anv_free_list_push(&pool->buckets[block_bucket].free_list,
+ &pool->table, st_idx, count);
+}
+
+/** Returns a chunk of memory back to the state pool.
+ *
+ * Do a two-level split. If chunk_size is bigger than divisor
+ * (pool->block_size), we return as many divisor sized blocks as we can, from
+ * the end of the chunk.
+ *
+ * The remaining is then split into smaller blocks (starting at small_size if
+ * it is non-zero), with larger blocks always being taken from the end of the
+ * chunk.
+ */
+static void
+anv_state_pool_return_chunk(struct anv_state_pool *pool,
+ uint32_t chunk_offset, uint32_t chunk_size,
+ uint32_t small_size)
+{
+ uint32_t divisor = pool->block_size;
+ uint32_t nblocks = chunk_size / divisor;
+ uint32_t rest = chunk_size - nblocks * divisor;
+
+ if (nblocks > 0) {
+ /* First return divisor aligned and sized chunks. We start returning
+ * larger blocks from the end fo the chunk, since they should already be
+ * aligned to divisor. Also anv_state_pool_return_blocks() only accepts
+ * aligned chunks.
+ */
+ uint32_t offset = chunk_offset + rest;
+ anv_state_pool_return_blocks(pool, offset, nblocks, divisor);
+ }
+
+ chunk_size = rest;
+ divisor /= 2;
+
+ if (small_size > 0 && small_size < divisor)
+ divisor = small_size;
+
+ uint32_t min_size = 1 << ANV_MIN_STATE_SIZE_LOG2;
+
+ /* Just as before, return larger divisor aligned blocks from the end of the
+ * chunk first.
+ */
+ while (chunk_size > 0 && divisor >= min_size) {
+ nblocks = chunk_size / divisor;
+ rest = chunk_size - nblocks * divisor;
+ if (nblocks > 0) {
+ anv_state_pool_return_blocks(pool, chunk_offset + rest,
+ nblocks, divisor);
+ chunk_size = rest;
+ }
+ divisor /= 2;
+ }
+}
+