On Fri, Feb 12, 2016 at 09:38:51AM +0000, Derek Morton wrote: > Adds functions to create a number of different batch buffers to perform > several functions including: > Batch buffer which will run for a long duration to provide a delay on a > specified ring. > Function to calibrate the delay batch buffer to run for a specified period > of time. > Function to create a batch buffer which writes timestamps to a buffer object. > Function to compare timestamps allowing for wrapping of the values. > > Intended for use by the gem_scheduler test initially but will be used by other > tests in development. > > Signed-off-by: Derek Morton <derek.j.morton@xxxxxxxxx> We already have batchbuffer factories and stuff in intel_batchbuffer.c. Integrating it there also relieves from adding a dummy and fairly useless topic section header in the docs. -Daniel > --- > lib/Makefile.sources | 2 + > lib/igt.h | 1 + > lib/igt_bb_factory.c | 401 +++++++++++++++++++++++++++++++++++++++++++++++++++ > lib/igt_bb_factory.h | 47 ++++++ > 4 files changed, 451 insertions(+) > create mode 100644 lib/igt_bb_factory.c > create mode 100644 lib/igt_bb_factory.h > > diff --git a/lib/Makefile.sources b/lib/Makefile.sources > index 4999868..c560b3e 100644 > --- a/lib/Makefile.sources > +++ b/lib/Makefile.sources > @@ -7,6 +7,8 @@ libintel_tools_la_SOURCES = \ > i915_reg.h \ > i915_pciids.h \ > igt.h \ > + igt_bb_factory.c \ > + igt_bb_factory.h \ > igt_debugfs.c \ > igt_debugfs.h \ > igt_aux.c \ > diff --git a/lib/igt.h b/lib/igt.h > index 3be2551..0f29420 100644 > --- a/lib/igt.h > +++ b/lib/igt.h > @@ -36,6 +36,7 @@ > #include "igt_gt.h" > #include "igt_kms.h" > #include "igt_stats.h" > +#include "igt_bb_factory.h" > #include "instdone.h" > #include "intel_batchbuffer.h" > #include "intel_chipset.h" > diff --git a/lib/igt_bb_factory.c b/lib/igt_bb_factory.c > new file mode 100644 > index 0000000..eea63c6 > --- /dev/null > +++ b/lib/igt_bb_factory.c > @@ -0,0 +1,401 @@ > +/* > + * Copyright © 2016 Intel Corporation > + * > + * 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: > + * Derek Morton <derek.j.morton@xxxxxxxxx> > + * > + */ > + > +#include "igt.h" > +#include "intel_batchbuffer.h" > +#include <stdint.h> > +#include <inttypes.h> > +#include <time.h> > + > +#define SEC_TO_NSEC (1000 * 1000 * 1000) > +#define DWORDS_TO_BYTES(x) ((x)*4) > + > +#define MI_STORE_REGISTER_MEM(LENGTH) ((0x024 << 23) | ((LENGTH - 2) & 0xff)) > +#define MI_MATH(NrInst) ((0x01A << 23) | ((NrInst - 1) & 0x3f)) > +#define MI_CONDITIONAL_BATCH_BUFFER_END ((0x036 << 23) | (1 << 21) | 2) > +#define MI_COPY_MEM_MEM ((0x02E << 23) | (3)) > + > +#define ALU_LOAD(TO, FROM) ((0x080 << 20) | ((TO) << 10) | (FROM)) > +#define ALU_SUB ( 0x101 << 20) > +#define ALU_STORE(TO, FROM) ((0x180 << 20) | ((TO) << 10) | (FROM)) > + > +#define TIMESTAMP_offset (0x358) /* Elapsed time from system start */ > +#define CTX_TIMESTAMP_offset (0x3A8) /* Elapsed Time from context creation */ > +#define ALU_GPU_R0_LSB_offset (0x600) > +#define ALU_GPU_R0_MSB_offset (0x604) > +#define ALU_GPU_R1_LSB_offset (0x608) > +#define ALU_GPU_R1_MSB_offset (0x60C) > +#define ALU_GPU_R2_LSB_offset (0x610) > +#define ALU_GPU_R2_MSB_offset (0x614) > + > +#define ALU_R0_ENCODING (0x00) > +#define ALU_R1_ENCODING (0x01) > +#define ALU_SRCA_ENCODING (0x20) > +#define ALU_SRCB_ENCODING (0x21) > +#define ALU_ACCU_ENCODING (0x31) > + > +/** > + * SECTION:igt_bb_factory > + * @short_description: Utility functions for creating batch buffers > + * @title: Batch Buffer Factory > + * @include: igt.h > + * > + * This library implements functions for creating batch buffers which may be > + * useful to multiple tests. > + */ > + > +static void check_gen_8(int fd) > +{ > + static bool checked = false; > + if(!checked) { > + igt_require(intel_gen(intel_get_drm_devid(fd)) >= 8); > + checked = true; > + } > +} > + > +static int bb_address_size_dw(int fd) > +{ > + if (intel_gen(intel_get_drm_devid(fd)) >= 8) > + return 2; > + else > + return 1; > +} > + > +static uint32_t get_register_offset(int ringid) > +{ > + switch (ringid) { > + case I915_EXEC_RENDER: > + return 0x02000; > + case I915_EXEC_BSD: > + return 0x12000; > + case I915_EXEC_BLT: > + return 0x22000; > + case I915_EXEC_VEBOX: > + return 0x1A000; > + default: > + igt_assert_f(0, "Invalid ringid %d passed to get_register_offset()\n", ringid); > + } > +} > + > +/** > + * igt_create_delay_bb: > + * @fd: file descriptor for i915 driver instance > + * @bufmgr: Buffer manager to be used for creation of batch buffers > + * ringid: Ring to create batch buffer for. e.g. I915_EXEC_RENDER > + * loops: Number of times to loop > + * dest: Buffer to use for saving the current loop count and timestamp. > + * > + * This creates a batch buffer which will iterate a loop a specified number > + * of times. Intended for creating batch buffers which take an arbitarily > + * long time to execute. This can be useful to keep a ring busy while > + * constructing a test scenario. > + * > + * The dest buffer will have a number of Dwords written by the batch buffer > + * when it runs. They are: > + * DW0 & DW1 - These are loade with the value of 'loops' and are decremented > + * as the batch buffer executes. They will be 0 after the batch > + * buffer completes if it finished succesfully. > + * DW2 Timestamp - An indication of when the batch buffer ran allowing a > + * comparison between batch buffers to show execution order. > + * May wrap so igt_compare_timestamps() should be used to > + * compare timestamps. > + * > + * Returns: > + * The struct intel_batchbuffer created. > + */ > +struct intel_batchbuffer *igt_create_delay_bb(int fd, drm_intel_bufmgr *bufmgr, > + int ringid, uint32_t loops, drm_intel_bo *dest) > +{ > + struct intel_batchbuffer *batch; > + int addr_size_dw; > + uint32_t regOffset; > + > + check_gen_8(fd); > + > + addr_size_dw = bb_address_size_dw(fd); > + regOffset = get_register_offset(ringid); > + batch = intel_batchbuffer_alloc(bufmgr, intel_get_drm_devid(fd)); > + igt_assert(batch); > + > + BEGIN_BATCH(32, 5); > + /* store current timestamp in DW2 */ > + OUT_BATCH(MI_STORE_REGISTER_MEM(addr_size_dw + 2)); > + OUT_BATCH(regOffset + TIMESTAMP_offset); > + OUT_RELOC(dest, I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION, DWORDS_TO_BYTES(2)); > + > + /* Load R0 with loops */ > + OUT_BATCH(MI_LOAD_REGISTER_IMM); > + OUT_BATCH(regOffset + ALU_GPU_R0_LSB_offset); > + OUT_BATCH(loops); > + OUT_BATCH(MI_LOAD_REGISTER_IMM); > + OUT_BATCH(regOffset + ALU_GPU_R0_MSB_offset); > + OUT_BATCH(0x00000000); > + /* Load R1 with 1 */ > + OUT_BATCH(MI_LOAD_REGISTER_IMM); > + OUT_BATCH(regOffset + ALU_GPU_R1_LSB_offset); > + OUT_BATCH(0x00000001); > + OUT_BATCH(MI_LOAD_REGISTER_IMM); > + OUT_BATCH(regOffset + ALU_GPU_R1_MSB_offset); > + OUT_BATCH(0x00000000); > + /* Copy R0 / R1 into SRCA / SRCB, Perform R0 - R1, Store result in R0 */ > + /* e.g. R0 -= 1 */ > + OUT_BATCH(MI_MATH(4)); > + OUT_BATCH(ALU_LOAD(ALU_SRCA_ENCODING, ALU_R0_ENCODING)); > + OUT_BATCH(ALU_LOAD(ALU_SRCB_ENCODING, ALU_R1_ENCODING)); > + OUT_BATCH(ALU_SUB); > + OUT_BATCH(ALU_STORE(ALU_R0_ENCODING, ALU_ACCU_ENCODING)); > + /* Copy R0 to dest > + * On Gen8 MI_CONDITIONAL_BATCH_BUFFER_END BSD ring Compare address > + * points to 2 Dwords, a mask (DW0) and data (DW1) which are ANDed > + * together. > + * On Gen9+, and the other rings on Gen8 Compare address points to > + * just Data (DW0). For simplicity always copy R0 LSB to DW0 and DW1. > + */ > + OUT_BATCH(MI_STORE_REGISTER_MEM(addr_size_dw + 2)); > + OUT_BATCH(regOffset + ALU_GPU_R0_LSB_offset); > + OUT_RELOC(dest, I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION, 0); > + OUT_BATCH(MI_STORE_REGISTER_MEM(addr_size_dw + 2)); > + OUT_BATCH(regOffset + ALU_GPU_R0_LSB_offset); > + OUT_RELOC(dest, I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION, DWORDS_TO_BYTES(1)); > + /* Repeat until R0 == 0 */ > + OUT_BATCH(MI_CONDITIONAL_BATCH_BUFFER_END); > + OUT_BATCH(0x00000000); > + OUT_RELOC(dest, I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION, 0); > + OUT_BATCH(MI_BATCH_BUFFER_START | (addr_size_dw - 1)); > + OUT_RELOC(batch->bo, I915_GEM_DOMAIN_INSTRUCTION, 0, DWORDS_TO_BYTES(15)); > + /* Should never get here, but end if it happens */ > + > + OUT_BATCH(MI_BATCH_BUFFER_END); > + ADVANCE_BATCH(); > + > + return batch; > +} > + > +/** > + * igt_create_timestamp_bb: > + * @fd: file descriptor for i915 driver instance > + * @bufmgr: Buffer manager to be used for creation of batch buffers > + * ringid: Ring to create batch buffer for. e.g. I915_EXEC_RENDER > + * dest: Buffer to use for saving the timestamps. > + * load: Buffer to access. Set NULL if not required. > + * write: If true and load is not NULL, will also write a timestamp to load > + * buffer. If false and load is not NULL, will read from load buffer into dest. > + * Intended for dependency checking. > + * > + * This creates a batch buffer which writes timestamps into a buffer object. > + * If 'load' is non null, data is either written to 'load' or copied from 'load' > + * depending on whether 'write' is set. > + * > + * The dest buffer will have a number of Dwords written by the batch buffer > + * when it runs. They are: > + * DW0 Reported timestamp - An indication of when the batch buffer ran allowing a > + * comparison between batch buffers to show execution order. > + * May wrap so igt_compare_timestamps() should be used to > + * compare timestamps. > + * DW1 Context timestamp - Elapsed time since context was created. > + * DW2 Value copied from DW0 of load if write == false > + * > + * Returns: > + * The struct intel_batchbuffer created. > + */ > +struct intel_batchbuffer *igt_create_timestamp_bb(int fd, drm_intel_bufmgr *bufmgr, > + int ringid, drm_intel_bo *dest, drm_intel_bo *load, bool write) > +{ > + struct intel_batchbuffer *batch; > + int addr_size_dw; > + uint32_t regOffset; > + > + check_gen_8(fd); > + > + addr_size_dw = bb_address_size_dw(fd); > + regOffset = get_register_offset(ringid); > + batch = intel_batchbuffer_alloc(bufmgr, intel_get_drm_devid(fd)); > + igt_assert(batch); > + > + BEGIN_BATCH(6, 2); > + /* store current reported timestamp in DW0 */ > + OUT_BATCH(MI_STORE_REGISTER_MEM(addr_size_dw + 2)); > + OUT_BATCH(regOffset + TIMESTAMP_offset); > + OUT_RELOC(dest, I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION, DWORDS_TO_BYTES(0)); > + > + /* store current context timestamp in DW1 */ > + OUT_BATCH(MI_STORE_REGISTER_MEM(addr_size_dw + 2)); > + OUT_BATCH(regOffset + CTX_TIMESTAMP_offset); > + OUT_RELOC(dest, I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION, DWORDS_TO_BYTES(1)); > + > + ADVANCE_BATCH(); > + > + if(load != NULL) { > + if(write) { > + BEGIN_BATCH(3, 1); > + OUT_BATCH(MI_STORE_REGISTER_MEM(addr_size_dw + 2)); > + OUT_BATCH(regOffset + TIMESTAMP_offset); > + OUT_RELOC(load, I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION, DWORDS_TO_BYTES(0)); > + ADVANCE_BATCH(); > + } > + else { > + BEGIN_BATCH(3, 2); > + OUT_BATCH(MI_COPY_MEM_MEM); > + OUT_RELOC(dest, I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION, DWORDS_TO_BYTES(2)); > + OUT_RELOC(load, I915_GEM_DOMAIN_INSTRUCTION, 0, DWORDS_TO_BYTES(0)); > + ADVANCE_BATCH(); > + } > + } > + > + BEGIN_BATCH(1, 0); > + OUT_BATCH(MI_BATCH_BUFFER_END); > + ADVANCE_BATCH(); > + > + return batch; > +} > + > +/** > + * igt_create_noop_bb: > + * @fd: file descriptor for i915 driver instance > + * @bufmgr: Buffer manager to be used for creation of batch buffers > + * ringid: Ring to create batch buffer for. e.g. I915_EXEC_RENDER > + * noops: Number of MI_NOOP instructions to add to the batch buffer. > + * > + * This creates a batch buffer with a specified number of MI_NOOP instructions. > + * > + * Returns: > + * The struct intel_batchbuffer created. > + */ > +struct intel_batchbuffer *igt_create_noop_bb(int fd, drm_intel_bufmgr *bufmgr, > + int ringid, int noops) > +{ > + struct intel_batchbuffer *batch; > + int loop; > + > + batch = intel_batchbuffer_alloc(bufmgr, intel_get_drm_devid(fd)); > + igt_assert(batch); > + > + BEGIN_BATCH(noops + 1, 0); > + for(loop = 0; loop < noops; loop++) > + OUT_BATCH(MI_NOOP); > + OUT_BATCH(MI_BATCH_BUFFER_END); > + ADVANCE_BATCH(); > + > + return batch; > +} > + > +/* Store calibrated values so they only need calculating once. > + * I915_EXEC_RING_MASK allows 3 bits for ring ids so allow for storing 8 values */ > +static uint32_t calibrated_ring_value[8] = {0, 0, 0, 0, 0, 0, 0, 0}; > + > +/** > + * igt_calibrate_delay_bb: > + * @fd: file descriptor for i915 driver instance > + * @bufmgr: Buffer manager to be used for creation of batch buffers > + * ringid: Ring to calibrate. e.g. I915_EXEC_RENDER > + * > + * This calculates the value of loops that would need to be passed to > + * igt_create_delay_bb() to create a delay of about 1 second on the specified > + * ring. > + * > + * Returns: > + * uint32_t to be passed to igt_create_delay_bb(). > + */ > +#define CAL_SEED (0x100000) > +uint32_t igt_calibrate_delay_bb(int fd, drm_intel_bufmgr *bufmgr, int ringid) > +{ > + uint32_t *buf; > + struct intel_batchbuffer *bb; > + struct timespec start, end; > + uint64_t duration; > + uint64_t calibrated; > + drm_intel_bo *target_bo; > + > + igt_assert(ringid < 8); > + if(calibrated_ring_value[ringid] != 0) > + return calibrated_ring_value[ringid]; > + > + target_bo = drm_intel_bo_alloc(bufmgr, "target bo", BATCH_SZ, BATCH_SZ); > + igt_assert(target_bo); > + > + /* Put some non zero values in the target bo */ > + drm_intel_bo_map(target_bo, 1); > + buf = target_bo->virtual; > + buf[0] = 0xff; > + drm_intel_bo_unmap(target_bo); > + > + bb = igt_create_delay_bb(fd, bufmgr, ringid, CAL_SEED, target_bo); > + > + gem_quiescent_gpu(fd); > + clock_gettime(CLOCK_MONOTONIC, &start); > + intel_batchbuffer_flush_on_ring(bb, ringid); > + /* This will not return until the bo has finished executing */ > + drm_intel_bo_map(target_bo, 0); > + clock_gettime(CLOCK_MONOTONIC, &end); > + > + buf = target_bo->virtual; > + /* buf[0] in the target buffer should be 0 if the batch buffer completed */ > + igt_assert_f(buf[0] == 0, "buf[0] expected 0x0, got 0x%x\n", buf[0]); > + > + duration = ((((uint64_t)end.tv_sec - (uint64_t)start.tv_sec) * SEC_TO_NSEC) > + + (uint64_t)end.tv_nsec) - (uint64_t)start.tv_nsec; > + > + calibrated = (((uint64_t)(CAL_SEED) * SEC_TO_NSEC) / duration); > + igt_debug("Uncalibrated run took %" PRIu64 ".%04" PRIu64 "s\n", > + duration / SEC_TO_NSEC, > + (duration % SEC_TO_NSEC) / 100000); > + drm_intel_bo_unreference(target_bo); > + intel_batchbuffer_free(bb); > + > + /* Sanity check. If duration < 100ms, something has clearly gone wrong > + * */ > + igt_assert(duration > (SEC_TO_NSEC / 10)); > + > + igt_assert_f(calibrated <= UINT32_MAX, "Calibrated value > UINT32_MAX\n"); > + > + calibrated_ring_value[ringid] = (uint32_t)calibrated; > + return (uint32_t)calibrated; > +} > + > +/** > + * igt_compare_timestamps: > + * @ts1: timestamp 1 > + * @ts2: timestamp 2 > + * > + * This compares two uint32_t timestamps. To handle wrapping it assumes the > + * difference between the two timestamps is less than 1/4 the max elapsed time > + * represented by the counters. > + * It also assumes the timestamps are samples from the same counter. > + * > + * Returns: > + * True if ts2 > ts1, allowing for counter wrapping, false otherwise. > + */ > + > +bool igt_compare_timestamps(uint32_t ts1, uint32_t ts2) > +{ > + if (ts2 > ts1) > + return true; > + else if ((ts1 > 0x80000000) && (ts2 < 0x40000000)) > + return true; /* Assuming timestamp counter wrapped */ > + else > + return false; > +} > diff --git a/lib/igt_bb_factory.h b/lib/igt_bb_factory.h > new file mode 100644 > index 0000000..3ab7f13 > --- /dev/null > +++ b/lib/igt_bb_factory.h > @@ -0,0 +1,47 @@ > +/* > + * Copyright © 2016 Intel Corporation > + * > + * 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: > + * Derek Morton <derek.j.morton@xxxxxxxxx> > + * > + */ > + > +#ifndef IGT_BB_FACTORY_H > +#define IGT_BB_FACTORY_H > + > +#include "intel_batchbuffer.h" > +#include <stdint.h> > + > +struct intel_batchbuffer *igt_create_delay_bb(int fd, drm_intel_bufmgr *bufmgr, > + int ringid, uint32_t loops, drm_intel_bo *dest); > + > +struct intel_batchbuffer *igt_create_timestamp_bb(int fd, drm_intel_bufmgr *bufmgr, > + int ringid, drm_intel_bo *dest, drm_intel_bo *load, bool write); > + > +struct intel_batchbuffer *igt_create_noop_bb(int fd, drm_intel_bufmgr *bufmgr, > + int ringid, int noops); > + > +uint32_t igt_calibrate_delay_bb(int fd, drm_intel_bufmgr *bufmgr, int ringid); > + > +bool igt_compare_timestamps(uint32_t ts1, uint32_t ts2); > + > +#endif /* IGT_BB_FACTORY_H */ > -- > 1.9.1 > -- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx