[Intel-gfx] [PATCH] igt/gem_trtt: Exercise the TRTT hardware

Chris Wilson chris at chris-wilson.co.uk
Mon Jan 11 04:32:08 PST 2016


On Sat, Jan 09, 2016 at 05:01:30PM +0530, akash.goel at intel.com wrote:
> +static void* mmap_bo(int fd, uint32_t handle, uint64_t size)
> +{
> +	uint32_t *ptr = gem_mmap__cpu(fd, handle, 0, size, PROT_READ);
> +	gem_set_domain(fd, handle, I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);

read-only mapping, but set to the cpu write domain? Seems inconsistent.

> +	return ptr;
> +}

> +/* gem_store_data_svm
> + * populate batch buffer with MI_STORE_DWORD_IMM command
> + * @fd: drm file descriptor
> + * @cmd_buf: batch buffer
> + * @dw_offset: write offset in batch buffer
> + * @vaddr: destination Virtual address
> + * @data: data to be store at destination
> + * @end: whether to end batch buffer or not
> +*/
> +static int gem_store_data_svm(int fd, uint32_t *cmd_buf, uint32_t dw_offset,
> +			uint64_t vaddr, uint32_t data, bool end)

Urm, what?

Just call this what it is,
emit_store_dword(cs, offset, vaddr, value);

Don't pass in bool end, since it is going to used exactly once and just
confuses all the other callers.

> +{
> +	cmd_buf[dw_offset++] = MI_STORE_DWORD_IMM;
> +	cmd_buf[dw_offset++] = vaddr & 0xFFFFFFFC;
> +	cmd_buf[dw_offset++] = (vaddr >> 32) & 0xFFFF; /* bits 32:47 */
> +
> +	cmd_buf[dw_offset++] = data;

Interesting use of whitespace.

> +	if (end) {
> +		cmd_buf[dw_offset++] = MI_BATCH_BUFFER_END;
> +		cmd_buf[dw_offset++] = 0;

> +	}
> +
> +	return dw_offset;
> +}
> +
> +/* setup_execbuffer
> + * helper for buffer execution
> + * @execbuf - pointer to execbuffer
> + * @exec_object - pointer to exec object2 struct
> + * @ring - ring to be used
> + * @buffer_count - how manu buffers to submit
> + * @batch_length - length of batch buffer
> +*/
> +static void setup_execbuffer(struct drm_i915_gem_execbuffer2 *execbuf,
> +			     struct drm_i915_gem_exec_object2 *exec_object,
> +			     int ring, int buffer_count, int batch_length)
> +{

How about memset(execbuf, 0, sizeof(*execbuf));

> +	execbuf->buffers_ptr = (unsigned long)exec_object;
> +	execbuf->buffer_count = buffer_count;
> +	execbuf->batch_start_offset = 0;
> +	execbuf->batch_len = batch_length;
> +	execbuf->cliprects_ptr = 0;
> +	execbuf->num_cliprects = 0;
> +	execbuf->DR1 = 0;
> +	execbuf->DR4 = 0;
> +	execbuf->flags = ring;
> +	i915_execbuffer2_set_context_id(*execbuf, 0);
> +	execbuf->rsvd2 = 0;
> +}
> +
> +/* submit_and_sync
> + * Helper function for exec and sync functions
> + * @fd - drm fd
> + * @execbuf - pointer to execbuffer
> + * @batch_buf_handle - batch buffer handle
> +*/
> +static void submit_and_sync(int fd, struct drm_i915_gem_execbuffer2 *execbuf,
> +			    uint32_t batch_buf_handle)
> +{
> +	gem_execbuf(fd, execbuf);
> +	gem_sync(fd, batch_buf_handle);

The only caller of this also does its own sync. This seems irrelevant
and serves a bad example.

> +}
> +
> +struct local_i915_gem_context_trtt_param {
> +	uint64_t l3_table_address;
> +	uint32_t invd_tile_val;
> +	uint32_t null_tile_val;
> +};
> +
> +/* send_trtt_params
> + * Helper function to request KMD to enable TRTT
> + * @fd - drm fd
> + * @ctx_id - id of the context for which TRTT is to be enabled
> + * @l3_table_address - GFX address of the L3 table
> +*/
> +static void send_trtt_params(int fd, uint32_t ctx_id, uint64_t l3_table_address)

It is not a socket, pipe, or other transport medium. Just setup_trtt().

> +#define TABLE_SIZE 0x1000
> +#define TILE_SIZE 0x10000
> +
> +#define FIRST_TILE_ADDRESS 0xF00000000000
> +#define LAST_TILE_ADDRESS  0xFFFFFFFF0000
> +
> +#define BO_ALLOC_AND_SETUP(fd, bo_size, bo_handle, bo_offset, idx) \
> +	bo_handle = gem_create(fd, bo_size); \
> +	bo_offset = current_ppgtt_offset; \
> +	setup_exec_obj(&exec_object2[idx], bo_handle, EXEC_OBJECT_PINNED, bo_offset); \
> +	current_ppgtt_offset += bo_size;

Function!

> +
> +/* basic test
> + * This test will add a series of MI_STORE_ commands, first to update the
> + * TR-TT table entries and then to update the data buffers using the TR-TT VA,
> + * exercising the programming the table programming done previously
> +*/
> +static void gem_basic_trtt_use(void)
> +{
> +	int fd;
> +	int ring, len = 0;
> +	uint32_t *ptr;
> +	struct drm_i915_gem_execbuffer2 execbuf;
> +	struct drm_i915_gem_exec_object2 exec_object2[8];
> +	uint32_t batch_buffer[BO_SIZE];
> +
> +	uint32_t l3_tbl_handle, l2_tbl1_handle, l2_tbl2_handle;
> +	uint32_t l1_tbl1_handle, l1_tbl2_handle, batch_buf_handle;
> +	uint32_t buffer1_handle, buffer2_handle;
> +
> +	uint64_t l3_tbl_offset, l2_tbl1_offset, l2_tbl2_offset;
> +	uint64_t l1_tbl1_offset, l1_tbl2_offset;
> +	uint64_t buffer1_offset, buffer2_offset;
> +
> +	uint32_t data;
> +	uint64_t address, current_ppgtt_offset = 0x10000;
> +
> +	fd = drm_open_driver(DRIVER_INTEL);
> +	igt_require(uses_full_ppgtt(fd, FULL_48_BIT_PPGTT));
> +	igt_require(has_softpin_support(fd));
> +	igt_require(has_trtt_support(fd));
> +
> +	/* Allocate a L3 table BO */
> +	BO_ALLOC_AND_SETUP(fd, TABLE_SIZE, l3_tbl_handle, l3_tbl_offset, 0);
> +
> +	/* Allocate two L2 table BOs */
> +	BO_ALLOC_AND_SETUP(fd, TABLE_SIZE, l2_tbl1_handle, l2_tbl1_offset, 1);
> +	BO_ALLOC_AND_SETUP(fd, TABLE_SIZE, l2_tbl2_handle, l2_tbl2_offset, 2);
> +
> +	/* Allocate two L1 table BOs */
> +	BO_ALLOC_AND_SETUP(fd, TABLE_SIZE, l1_tbl1_handle, l1_tbl1_offset, 3);
> +	BO_ALLOC_AND_SETUP(fd, TABLE_SIZE, l1_tbl2_handle, l1_tbl2_offset, 4);
> +
> +	/* Align the PPGTT offsets for the 2 data buffers to next 64 KB boundary */
> +	current_ppgtt_offset = ALIGN(current_ppgtt_offset, TILE_SIZE);
> +
> +	/* Allocate two Data buffer BOs */
> +	BO_ALLOC_AND_SETUP(fd, TILE_SIZE, buffer1_handle, buffer1_offset, 5);
> +	BO_ALLOC_AND_SETUP(fd, TILE_SIZE, buffer2_handle, buffer2_offset, 6);
> +
> +	/* Finally allocate Batch buffer BO */
> +	batch_buf_handle = gem_create(fd, BO_SIZE);
> +	setup_exec_obj(&exec_object2[7], batch_buf_handle, 0, 0);

Scary jump from idx to 7.
Why not just pin this as well to reduce the code complexity? Afterwards
setup_exec_obj() can allocate an object all by itself.

> +
> +	/* Add commands to update the two L3 table entries to point them to the L2 tables*/
> +	address = l3_tbl_offset;
> +	data = l2_tbl1_offset;
> +	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
> +
> +	address = l3_tbl_offset + 511*sizeof(uint64_t);
> +	data = l2_tbl2_offset;
> +	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
> +
> +	/* Add commands to update an entry of 2 L2 tables to point them to the L1 tables*/
> +	address = l2_tbl1_offset;
> +	data = l1_tbl1_offset;
> +	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
> +
> +	address = l2_tbl2_offset + 511*sizeof(uint64_t);
> +	data = l1_tbl2_offset;
> +	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
> +
> +	/* Add commands to update an entry of 2 L1 tables to point them to the data buffers*/
> +	address = l1_tbl1_offset;
> +	data = buffer1_offset >> 16;
> +	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
> +
> +	address = l1_tbl2_offset + 1023*sizeof(uint32_t);
> +	data = buffer2_offset >> 16;
> +	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
> +
> +	/* Add commands to update the 2 data buffers, using their TRTT VA */
> +	data = 0x12345678;
> +	len = gem_store_data_svm(fd, batch_buffer, len, FIRST_TILE_ADDRESS, data, false);
> +	len = gem_store_data_svm(fd, batch_buffer, len, LAST_TILE_ADDRESS, data, true);
> +
> +	gem_write(fd, batch_buf_handle, 0, batch_buffer, len*4);

Or for even shorter code: batch_buffer =
gem_mmap__cpu(exec_object[batch].handle);

> +
> +	/* Request KMD to setup the TR-TT */
> +	send_trtt_params(fd, 0, l3_tbl_offset);
> +
> +	ring = I915_EXEC_RENDER;
> +	setup_execbuffer(&execbuf, exec_object2, ring, 8, len*4);
> +
> +	/* submit command buffer */
> +	submit_and_sync(fd, &execbuf, batch_buf_handle);
> +
> +	/* read the 2 data buffers to check for the value written by the GPU */
> +	ptr = mmap_bo(fd, buffer1_handle, TILE_SIZE);
> +	igt_fail_on_f(ptr[0] != data,
> +		"\nCPU read does not match GPU write,\
> +		expected: 0x%x, got: 0x%x\n",
> +		data, ptr[0]);
> +
> +	ptr = mmap_bo(fd, buffer2_handle, TILE_SIZE);
> +	igt_fail_on_f(ptr[0] != data,
> +		"\nCPU read does not match GPU write,\
> +		expected: 0x%x, got: 0x%x\n",
> +		data, ptr[0]);
> +
> +	gem_close(fd, l3_tbl_handle);
> +	gem_close(fd, l2_tbl1_handle);
> +	gem_close(fd, l2_tbl2_handle);
> +	gem_close(fd, l1_tbl1_handle);
> +	gem_close(fd, l1_tbl2_handle);
> +	gem_close(fd, buffer1_handle);
> +	gem_close(fd, buffer2_handle);
> +	gem_close(fd, batch_buf_handle);
> +	close(fd);
> +}
> +
> +int main(int argc, char* argv[])
> +{
> +	igt_subtest_init(argc, argv);

Together these are igt_main, and then you can also drop igt_exit.

> +	igt_skip_on_simulation();

I think you want this on simulation as well, as least "basic".

> +
> +	/* test needs 48 PPGTT & Soft Pin support */
> +	igt_subtest("basic") {
> +		gem_basic_trtt_use();
> +	}
> +
> +	igt_exit();
> +}
> +
> -- 
> 1.9.2
> 

-- 
Chris Wilson, Intel Open Source Technology Centre


More information about the Intel-gfx mailing list