/** * DOC: i915 Perf Overview * * Gen graphics supports a large number of performance counters that can help * driver and application developers understand and optimize their use of the * GPU. * * This i915 perf interface enables userspace to configure and open a file * descriptor representing a stream of GPU metrics which can then be read() as * a stream of sample records. * * The interface is particularly suited to exposing buffered metrics that are * captured by DMA from the GPU, unsynchronized with and unrelated to the CPU. * * Streams representing a single context are accessible to applications with a * corresponding drm file descriptor, such that OpenGL can use the interface * without special privileges. Access to system-wide metrics requires root * privileges by default, unless changed via the dev.i915.perf_event_paranoid * sysctl option. *
*/
/** * DOC: i915 Perf History and Comparison with Core Perf * * The interface was initially inspired by the core Perf infrastructure but * some notable differences are: * * i915 perf file descriptors represent a "stream" instead of an "event"; where * a perf event primarily corresponds to a single 64bit value, while a stream * might sample sets of tightly-coupled counters, depending on the * configuration. For example the Gen OA unit isn't designed to support * orthogonal configurations of individual counters; it's configured for a set * of related counters. Samples for an i915 perf stream capturing OA metrics * will include a set of counter values packed in a compact HW specific format. * The OA unit supports a number of different packing formats which can be * selected by the user opening the stream. Perf has support for grouping * events, but each event in the group is configured, validated and * authenticated individually with separate system calls. * * i915 perf stream configurations are provided as an array of u64 (key,value) * pairs, instead of a fixed struct with multiple miscellaneous config members, * interleaved with event-type specific members. * * i915 perf doesn't support exposing metrics via an mmap'd circular buffer. * The supported metrics are being written to memory by the GPU unsynchronized * with the CPU, using HW specific packing formats for counter sets. Sometimes * the constraints on HW configuration require reports to be filtered before it * would be acceptable to expose them to unprivileged applications - to hide * the metrics of other processes/contexts. For these use cases a read() based * interface is a good fit, and provides an opportunity to filter data as it * gets copied from the GPU mapped buffers to userspace buffers. * * * Issues hit with first prototype based on Core Perf * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * The first prototype of this driver was based on the core perf * infrastructure, and while we did make that mostly work, with some changes to * perf, we found we were breaking or working around too many assumptions baked * into perf's currently cpu centric design. * * In the end we didn't see a clear benefit to making perf's implementation and * interface more complex by changing design assumptions while we knew we still * wouldn't be able to use any existing perf based userspace tools. * * Also considering the Gen specific nature of the Observability hardware and * how userspace will sometimes need to combine i915 perf OA metrics with * side-band OA data captured via MI_REPORT_PERF_COUNT commands; we're * expecting the interface to be used by a platform specific userspace such as * OpenGL or tools. This is to say; we aren't inherently missing out on having * a standard vendor/architecture agnostic interface by not using perf. * * * For posterity, in case we might re-visit trying to adapt core perf to be * better suited to exposing i915 metrics these were the main pain points we * hit: * * - The perf based OA PMU driver broke some significant design assumptions: * * Existing perf pmus are used for profiling work on a cpu and we were * introducing the idea of _IS_DEVICE pmus with different security * implications, the need to fake cpu-related data (such as user/kernel * registers) to fit with perf's current design, and adding _DEVICE records * as a way to forward device-specific status records. * * The OA unit writes reports of counters into a circular buffer, without * involvement from the CPU, making our PMU driver the first of a kind. * * Given the way we were periodically forward data from the GPU-mapped, OA * buffer to perf's buffer, those bursts of sample writes looked to perf like * we were sampling too fast and so we had to subvert its throttling checks. * * Perf supports groups of counters and allows those to be read via * transactions internally but transactions currently seem designed to be * explicitly initiated from the cpu (say in response to a userspace read()) * and while we could pull a report out of the OA buffer we can't * trigger a report from the cpu on demand. * * Related to being report based; the OA counters are configured in HW as a * set while perf generally expects counter configurations to be orthogonal. * Although counters can be associated with a group leader as they are * opened, there's no clear precedent for being able to provide group-wide * configuration attributes (for example we want to let userspace choose the * OA unit report format used to capture all counters in a set, or specify a * GPU context to filter metrics on). We avoided using perf's grouping * feature and forwarded OA reports to userspace via perf's 'raw' sample * field. This suited our userspace well considering how coupled the counters * are when dealing with normalizing. It would be inconvenient to split * counters up into separate events, only to require userspace to recombine * them. For Mesa it's also convenient to be forwarded raw, periodic reports * for combining with the side-band raw reports it captures using * MI_REPORT_PERF_COUNT commands. * * - As a side note on perf's grouping feature; there was also some concern * that using PERF_FORMAT_GROUP as a way to pack together counter values * would quite drastically inflate our sample sizes, which would likely * lower the effective sampling resolutions we could use when the available * memory bandwidth is limited. * * With the OA unit's report formats, counters are packed together as 32 * or 40bit values, with the largest report size being 256 bytes. * * PERF_FORMAT_GROUP values are 64bit, but there doesn't appear to be a * documented ordering to the values, implying PERF_FORMAT_ID must also be * used to add a 64bit ID before each value; giving 16 bytes per counter. * * Related to counter orthogonality; we can't time share the OA unit, while * event scheduling is a central design idea within perf for allowing * userspace to open + enable more events than can be configured in HW at any * one time. The OA unit is not designed to allow re-configuration while in * use. We can't reconfigure the OA unit without losing internal OA unit * state which we can't access explicitly to save and restore. Reconfiguring * the OA unit is also relatively slow, involving ~100 register writes. From * userspace Mesa also depends on a stable OA configuration when emitting * MI_REPORT_PERF_COUNT commands and importantly the OA unit can't be * disabled while there are outstanding MI_RPC commands lest we hang the * command streamer. * * The contents of sample records aren't extensible by device drivers (i.e. * the sample_type bits). As an example; Sourab Gupta had been looking to * attach GPU timestamps to our OA samples. We were shoehorning OA reports * into sample records by using the 'raw' field, but it's tricky to pack more * than one thing into this field because events/core.c currently only lets a * pmu give a single raw data pointer plus len which will be copied into the * ring buffer. To include more than the OA report we'd have to copy the * report into an intermediate larger buffer. I'd been considering allowing a * vector of data+len values to be specified for copying the raw data, but * it felt like a kludge to being using the raw field for this purpose. * * - It felt like our perf based PMU was making some technical compromises * just for the sake of using perf: * * perf_event_open() requires events to either relate to a pid or a specific * cpu core, while our device pmu related to neither. Events opened with a * pid will be automatically enabled/disabled according to the scheduling of * that process - so not appropriate for us. When an event is related to a * cpu id, perf ensures pmu methods will be invoked via an inter process * interrupt on that core. To avoid invasive changes our userspace opened OA * perf events for a specific cpu. This was workable but it meant the * majority of the OA driver ran in atomic context, including all OA report * forwarding, which wasn't really necessary in our case and seems to make * our locking requirements somewhat complex as we handled the interaction * with the rest of the i915 driver.
*/
/* HW requires this to be a power of two, between 128k and 16M, though driver * is currently generally designed assuming the largest 16M size is used such * that the overflow cases are unlikely in normal operation.
*/ #define OA_BUFFER_SIZE SZ_16M
/** * DOC: OA Tail Pointer Race * * There's a HW race condition between OA unit tail pointer register updates and * writes to memory whereby the tail pointer can sometimes get ahead of what's * been written out to the OA buffer so far (in terms of what's visible to the * CPU). * * Although this can be observed explicitly while copying reports to userspace * by checking for a zeroed report-id field in tail reports, we want to account * for this earlier, as part of the oa_buffer_check_unlocked to avoid lots of * redundant read() attempts. * * We workaround this issue in oa_buffer_check_unlocked() by reading the reports * in the OA buffer, starting from the tail reported by the HW until we find a * report with its first 2 dwords not 0 meaning its previous report is * completely in memory and ready to be read. Those dwords are also set to 0 * once read and the whole buffer is cleared upon OA buffer initialization. The * first dword is the reason for this report while the second is the timestamp, * making the chances of having those 2 fields at 0 fairly unlikely. A more * detailed explanation is available in oa_buffer_check_unlocked(). * * Most of the implementation details for this workaround are in * oa_buffer_check_unlocked() and _append_oa_reports() * * Note for posterity: previously the driver used to define an effective tail * pointer that lagged the real pointer by a 'tail margin' measured in bytes * derived from %OA_TAIL_MARGIN_NSEC and the configured sampling frequency. * This was flawed considering that the OA unit may also automatically generate * non-periodic reports (such as on context switch) or the OA unit may be * enabled without any periodic sampling.
*/ #define OA_TAIL_MARGIN_NSEC 100000ULL #define INVALID_TAIL_PTR 0xffffffff
/* The default frequency for checking whether the OA unit has written new * reports to the circular OA buffer...
*/ #define DEFAULT_POLL_FREQUENCY_HZ 200 #define DEFAULT_POLL_PERIOD_NS (NSEC_PER_SEC / DEFAULT_POLL_FREQUENCY_HZ)
/* for sysctl proc_dointvec_minmax of dev.i915.perf_stream_paranoid */ static u32 i915_perf_stream_paranoid = true;
/* The maximum exponent the hardware accepts is 63 (essentially it selects one * of the 64bit timestamp bits to trigger reports from) but there's currently * no known use case for sampling as infrequently as once per 47 thousand years. * * Since the timestamps included in OA reports are only 32bits it seems * reasonable to limit the OA exponent where it's still possible to account for * overflow in OA report timestamps.
*/ #define OA_EXPONENT_MAX 31
#define INVALID_CTX_ID 0xffffffff
/* On Gen8+ automatically triggered OA reports include a 'reason' field... */ #define OAREPORT_REASON_MASK 0x3f #define OAREPORT_REASON_MASK_EXTENDED 0x7f #define OAREPORT_REASON_SHIFT 19 #define OAREPORT_REASON_TIMER (1<<0) #define OAREPORT_REASON_CTX_SWITCH (1<<3) #define OAREPORT_REASON_CLK_RATIO (1<<5)
/* For sysctl proc_dointvec_minmax of i915_oa_max_sample_rate * * The highest sampling frequency we can theoretically program the OA unit * with is always half the timestamp frequency: E.g. 6.25Mhz for Haswell. * * Initialized just before we register the sysctl parameter.
*/ staticint oa_sample_rate_hard_limit;
/* Theoretically we can program the OA unit to sample every 160ns but don't * allow that by default unless root... * * The default threshold of 100000Hz is based on perf's similar * kernel.perf_event_max_sample_rate sysctl parameter.
*/ static u32 i915_oa_max_sample_rate = 100000;
/* XXX: beware if future OA HW adds new report formats that the current * code assumes all reports have a power-of-two size and ~(size - 1) can * be used as a mask to align the OA tail pointer.
*/ staticconststruct i915_oa_format oa_formats[I915_OA_FORMAT_MAX] = {
[I915_OA_FORMAT_A13] = { 0, 64 },
[I915_OA_FORMAT_A29] = { 1, 128 },
[I915_OA_FORMAT_A13_B8_C8] = { 2, 128 }, /* A29_B8_C8 Disallowed as 192 bytes doesn't factor into buffer size */
[I915_OA_FORMAT_B4_C8] = { 4, 64 },
[I915_OA_FORMAT_A45_B8_C8] = { 5, 256 },
[I915_OA_FORMAT_B4_C8_A16] = { 6, 128 },
[I915_OA_FORMAT_C4_B8] = { 7, 64 },
[I915_OA_FORMAT_A12] = { 0, 64 },
[I915_OA_FORMAT_A12_B8_C8] = { 2, 128 },
[I915_OA_FORMAT_A32u40_A4u32_B8_C8] = { 5, 256 },
[I915_OAR_FORMAT_A32u40_A4u32_B8_C8] = { 5, 256 },
[I915_OA_FORMAT_A24u40_A14u32_B8_C8] = { 5, 256 },
[I915_OAM_FORMAT_MPEC8u64_B8_C8] = { 1, 192, TYPE_OAM, HDR_64_BIT },
[I915_OAM_FORMAT_MPEC8u32_B8_C8] = { 2, 128, TYPE_OAM, HDR_64_BIT },
};
/** * struct perf_open_properties - for validated properties given to open a stream * @sample_flags: `DRM_I915_PERF_PROP_SAMPLE_*` properties are tracked as flags * @single_context: Whether a single or all gpu contexts should be monitored * @hold_preemption: Whether the preemption is disabled for the filtered * context * @ctx_handle: A gem ctx handle for use with @single_context * @metrics_set: An ID for an OA unit metric set advertised via sysfs * @oa_format: An OA unit HW report format * @oa_periodic: Whether to enable periodic OA unit sampling * @oa_period_exponent: The OA unit sampling period is derived from this * @engine: The engine (typically rcs0) being monitored by the OA unit * @has_sseu: Whether @sseu was specified by userspace * @sseu: internal SSEU configuration computed either from the userspace * specified configuration in the opening parameters or a default value * (see get_default_sseu_config()) * @poll_oa_period: The period in nanoseconds at which the CPU will check for OA * data availability * * As read_properties_unlocked() enumerates and validates the properties given * to open a stream of metrics the configuration is built up in the structure * which starts out zero initialized.
*/ struct perf_open_properties {
u32 sample_flags;
/** * oa_buffer_check_unlocked - check for data and update tail ptr state * @stream: i915 stream instance * * This is either called via fops (for blocking reads in user ctx) or the poll * check hrtimer (atomic ctx) to check the OA buffer tail pointer and check * if there is data available for userspace to read. * * This function is central to providing a workaround for the OA unit tail * pointer having a race with respect to what data is visible to the CPU. * It is responsible for reading tail pointers from the hardware and giving * the pointers time to 'age' before they are made available for reading. * (See description of OA_TAIL_MARGIN_NSEC above for further details.) * * Besides returning true when there is data available to read() this function * also updates the tail in the oa_buffer object. * * Note: It's safe to read OA config state here unlocked, assuming that this is * only called while the stream is enabled, while the global OA configuration * can't be modified. * * Returns: %true if the OA buffer contains data, else %false
*/ staticbool oa_buffer_check_unlocked(struct i915_perf_stream *stream)
{
u32 gtt_offset = i915_ggtt_offset(stream->oa_buffer.vma); int report_size = stream->oa_buffer.format->size;
u32 tail, hw_tail; unsignedlong flags; bool pollin;
u32 partial_report_size;
/* * We have to consider the (unlikely) possibility that read() errors * could result in an OA buffer reset which might reset the head and * tail state.
*/
spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
/* * The tail pointer increases in 64 byte increments, not in report_size * steps. Also the report size may not be a power of 2. Compute * potentially partially landed report in the OA buffer
*/
partial_report_size = OA_TAKEN(hw_tail, stream->oa_buffer.tail);
partial_report_size %= report_size;
/* Subtract partial amount off the tail */
hw_tail = OA_TAKEN(hw_tail, partial_report_size);
tail = hw_tail;
/* * Walk the stream backward until we find a report with report * id and timestamp not at 0. Since the circular buffer pointers * progress by increments of 64 bytes and that reports can be up * to 256 bytes long, we can't tell whether a report has fully * landed in memory before the report id and timestamp of the * following report have effectively landed. * * This is assuming that the writes of the OA unit land in * memory in the order they were written to. * If not : (╯°□°)╯︵ ┻━┻
*/ while (OA_TAKEN(tail, stream->oa_buffer.tail) >= report_size) { void *report = stream->oa_buffer.vaddr + tail;
if (oa_report_id(stream, report) ||
oa_timestamp(stream, report)) break;
/** * append_oa_status - Appends a status record to a userspace read() buffer. * @stream: An i915-perf stream opened for OA metrics * @buf: destination buffer given by userspace * @count: the number of bytes userspace wants to read * @offset: (inout): the current position for writing into @buf * @type: The kind of status to report to userspace * * Writes a status record (such as `DRM_I915_PERF_RECORD_OA_REPORT_LOST`) * into the userspace read() buffer. * * The @buf @offset will only be updated on success. * * Returns: 0 on success, negative error code on failure.
*/ staticint append_oa_status(struct i915_perf_stream *stream, char __user *buf,
size_t count,
size_t *offset, enum drm_i915_perf_record_type type)
{ struct drm_i915_perf_record_header header = { type, 0, sizeof(header) };
if ((count - *offset) < header.size) return -ENOSPC;
if (copy_to_user(buf + *offset, &header, sizeof(header))) return -EFAULT;
(*offset) += header.size;
return 0;
}
/** * append_oa_sample - Copies single OA report into userspace read() buffer. * @stream: An i915-perf stream opened for OA metrics * @buf: destination buffer given by userspace * @count: the number of bytes userspace wants to read * @offset: (inout): the current position for writing into @buf * @report: A single OA report to (optionally) include as part of the sample * * The contents of a sample are configured through `DRM_I915_PERF_PROP_SAMPLE_*` * properties when opening a stream, tracked as `stream->sample_flags`. This * function copies the requested components of a single sample to the given * read() @buf. * * The @buf @offset will only be updated on success. * * Returns: 0 on success, negative error code on failure.
*/ staticint append_oa_sample(struct i915_perf_stream *stream, char __user *buf,
size_t count,
size_t *offset, const u8 *report)
{ int report_size = stream->oa_buffer.format->size; struct drm_i915_perf_record_header header; int report_size_partial;
u8 *oa_buf_end;
/** * gen8_append_oa_reports - Copies all buffered OA reports into * userspace read() buffer. * @stream: An i915-perf stream opened for OA metrics * @buf: destination buffer given by userspace * @count: the number of bytes userspace wants to read * @offset: (inout): the current position for writing into @buf * * Notably any error condition resulting in a short read (-%ENOSPC or * -%EFAULT) will be returned even though one or more records may * have been successfully copied. In this case it's up to the caller * to decide if the error should be squashed before returning to * userspace. * * Note: reports are consumed from the head, and appended to the * tail, so the tail chases the head?... If you think that's mad * and back-to-front you're not alone, but this follows the * Gen PRM naming convention. * * Returns: 0 on success, negative error code on failure.
*/ staticint gen8_append_oa_reports(struct i915_perf_stream *stream, char __user *buf,
size_t count,
size_t *offset)
{ struct intel_uncore *uncore = stream->uncore; int report_size = stream->oa_buffer.format->size;
u8 *oa_buf_base = stream->oa_buffer.vaddr;
u32 gtt_offset = i915_ggtt_offset(stream->oa_buffer.vma);
u32 mask = (OA_BUFFER_SIZE - 1);
size_t start_offset = *offset; unsignedlong flags;
u32 head, tail; int ret = 0;
if (drm_WARN_ON(&uncore->i915->drm, !stream->enabled)) return -EIO;
/* * An out of bounds or misaligned head or tail pointer implies a driver * bug since we validate + align the tail pointers we read from the * hardware and we are in full control of the head pointer which should * only be incremented by multiples of the report size.
*/ if (drm_WARN_ONCE(&uncore->i915->drm,
head > OA_BUFFER_SIZE ||
tail > OA_BUFFER_SIZE, "Inconsistent OA buffer pointers: head = %u, tail = %u\n",
head, tail)) return -EIO;
/* * The reason field includes flags identifying what * triggered this specific report (mostly timer * triggered or e.g. due to a context switch).
*/
reason = oa_report_reason(stream, report);
ctx_id = oa_context_id(stream, report32);
/* * Squash whatever is in the CTX_ID field if it's marked as * invalid to be sure we avoid false-positive, single-context * filtering below... * * Note: that we don't clear the valid_ctx_bit so userspace can * understand that the ID has been squashed by the kernel. * * Update: * * On XEHP platforms the behavior of context id valid bit has * changed compared to prior platforms. To describe this, we * define a few terms: * * context-switch-report: This is a report with the reason type * being context-switch. It is generated when a context switches * out. * * context-valid-bit: A bit that is set in the report ID field * to indicate that a valid context has been loaded. * * gpu-idle: A condition characterized by a * context-switch-report with context-valid-bit set to 0. * * On prior platforms, context-id-valid bit is set to 0 only * when GPU goes idle. In all other reports, it is set to 1. * * On XEHP platforms, context-valid-bit is set to 1 in a context * switch report if a new context switched in. For all other * reports it is set to 0. * * This change in behavior causes an issue with MMIO triggered * reports. MMIO triggered reports have the markers in the * context ID field and the context-valid-bit is 0. The logic * below to squash the context ID would render the report * useless since the user will not be able to find it in the OA * buffer. Since MMIO triggered reports exist only on XEHP, * we should avoid squashing these for XEHP platforms.
*/
/* * NB: For Gen 8 the OA unit no longer supports clock gating * off for a specific context and the kernel can't securely * stop the counters from updating as system-wide / global * values. * * Automatic reports now include a context ID so reports can be * filtered on the cpu but it's not worth trying to * automatically subtract/hide counter progress for other * contexts while filtering since we can't stop userspace * issuing MI_REPORT_PERF_COUNT commands which would still * provide a side-band view of the real values. * * To allow userspace (such as Mesa/GL_INTEL_performance_query) * to normalize counters for a single filtered context then it * needs be forwarded bookend context-switch reports so that it * can track switches in between MI_REPORT_PERF_COUNT commands * and can itself subtract/ignore the progress of counters * associated with other contexts. Note that the hardware * automatically triggers reports when switching to a new * context which are tagged with the ID of the newly active * context. To avoid the complexity (and likely fragility) of * reading ahead while parsing reports to try and minimize * forwarding redundant context switch reports (i.e. between * other, unrelated contexts) we simply elect to forward them * all. * * We don't rely solely on the reason field to identify context * switches since it's not-uncommon for periodic samples to * identify a switch before any 'context switch' report.
*/ if (!stream->ctx ||
stream->specific_ctx_id == ctx_id ||
stream->oa_buffer.last_ctx_id == stream->specific_ctx_id ||
reason & OAREPORT_REASON_CTX_SWITCH) {
/* * While filtering for a single context we avoid * leaking the IDs of other contexts.
*/ if (stream->ctx &&
stream->specific_ctx_id != ctx_id) {
oa_context_id_squash(stream, report32);
}
ret = append_oa_sample(stream, buf, count, offset,
report); if (ret) break;
stream->oa_buffer.last_ctx_id = ctx_id;
}
if (is_power_of_2(report_size)) { /* * Clear out the report id and timestamp as a means * to detect unlanded reports.
*/
oa_report_id_clear(stream, report32);
oa_timestamp_clear(stream, report32);
} else {
u8 *oa_buf_end = stream->oa_buffer.vaddr +
OA_BUFFER_SIZE;
u32 part = oa_buf_end - (u8 *)report32;
/* Zero out the entire report */ if (report_size <= part) {
memset(report32, 0, report_size);
} else {
memset(report32, 0, part);
memset(oa_buf_base, 0, report_size - part);
}
}
}
if (start_offset != *offset) {
i915_reg_t oaheadptr;
/* * We removed the gtt_offset for the copy loop above, indexing * relative to oa_buf_base so put back here...
*/
intel_uncore_write(uncore, oaheadptr,
(head + gtt_offset) & GEN12_OAG_OAHEADPTR_MASK);
stream->oa_buffer.head = head;
/** * gen8_oa_read - copy status records then buffered OA reports * @stream: An i915-perf stream opened for OA metrics * @buf: destination buffer given by userspace * @count: the number of bytes userspace wants to read * @offset: (inout): the current position for writing into @buf * * Checks OA unit status registers and if necessary appends corresponding * status records for userspace (such as for a buffer full condition) and then * initiate appending any buffered OA reports. * * Updates @offset according to the number of bytes successfully copied into * the userspace buffer. * * NB: some data may be successfully copied to the userspace buffer * even if an error is returned, and this is reflected in the * updated @offset. * * Returns: zero on success or a negative error code
*/ staticint gen8_oa_read(struct i915_perf_stream *stream, char __user *buf,
size_t count,
size_t *offset)
{ struct intel_uncore *uncore = stream->uncore;
u32 oastatus;
i915_reg_t oastatus_reg; int ret;
if (drm_WARN_ON(&uncore->i915->drm, !stream->oa_buffer.vaddr)) return -EIO;
/* * We treat OABUFFER_OVERFLOW as a significant error: * * Although theoretically we could handle this more gracefully * sometimes, some Gens don't correctly suppress certain * automatically triggered reports in this condition and so we * have to assume that old reports are now being trampled * over. * * Considering how we don't currently give userspace control * over the OA buffer size and always configure a large 16MB * buffer, then a buffer overflow does anyway likely indicate * that something has gone quite badly wrong.
*/ if (oastatus & GEN8_OASTATUS_OABUFFER_OVERFLOW) {
ret = append_oa_status(stream, buf, count, offset,
DRM_I915_PERF_RECORD_OA_BUFFER_LOST); if (ret) return ret;
drm_dbg(&stream->perf->i915->drm, "OA buffer overflow (exponent = %d): force restart\n",
stream->period_exponent);
/** * gen7_append_oa_reports - Copies all buffered OA reports into * userspace read() buffer. * @stream: An i915-perf stream opened for OA metrics * @buf: destination buffer given by userspace * @count: the number of bytes userspace wants to read * @offset: (inout): the current position for writing into @buf * * Notably any error condition resulting in a short read (-%ENOSPC or * -%EFAULT) will be returned even though one or more records may * have been successfully copied. In this case it's up to the caller * to decide if the error should be squashed before returning to * userspace. * * Note: reports are consumed from the head, and appended to the * tail, so the tail chases the head?... If you think that's mad * and back-to-front you're not alone, but this follows the * Gen PRM naming convention. * * Returns: 0 on success, negative error code on failure.
*/ staticint gen7_append_oa_reports(struct i915_perf_stream *stream, char __user *buf,
size_t count,
size_t *offset)
{ struct intel_uncore *uncore = stream->uncore; int report_size = stream->oa_buffer.format->size;
u8 *oa_buf_base = stream->oa_buffer.vaddr;
u32 gtt_offset = i915_ggtt_offset(stream->oa_buffer.vma);
u32 mask = (OA_BUFFER_SIZE - 1);
size_t start_offset = *offset; unsignedlong flags;
u32 head, tail; int ret = 0;
if (drm_WARN_ON(&uncore->i915->drm, !stream->enabled)) return -EIO;
/* An out of bounds or misaligned head or tail pointer implies a driver * bug since we validate + align the tail pointers we read from the * hardware and we are in full control of the head pointer which should * only be incremented by multiples of the report size (notably also * all a power of two).
*/ if (drm_WARN_ONCE(&uncore->i915->drm,
head > OA_BUFFER_SIZE || head % report_size ||
tail > OA_BUFFER_SIZE || tail % report_size, "Inconsistent OA buffer pointers: head = %u, tail = %u\n",
head, tail)) return -EIO;
/* All the report sizes factor neatly into the buffer * size so we never expect to see a report split * between the beginning and end of the buffer. * * Given the initial alignment check a misalignment * here would imply a driver bug that would result * in an overrun.
*/ if (drm_WARN_ON(&uncore->i915->drm,
(OA_BUFFER_SIZE - head) < report_size)) {
drm_err(&uncore->i915->drm, "Spurious OA head ptr: non-integral report offset\n"); break;
}
/* The report-ID field for periodic samples includes * some undocumented flags related to what triggered * the report and is never expected to be zero so we * can check that the report isn't invalid before * copying it to userspace...
*/ if (report32[0] == 0) { if (__ratelimit(&stream->perf->spurious_report_rs))
drm_notice(&uncore->i915->drm, "Skipping spurious, invalid OA report\n"); continue;
}
ret = append_oa_sample(stream, buf, count, offset, report); if (ret) break;
/* Clear out the first 2 dwords as a mean to detect unlanded * reports.
*/
report32[0] = 0;
report32[1] = 0;
}
if (start_offset != *offset) {
spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
/** * gen7_oa_read - copy status records then buffered OA reports * @stream: An i915-perf stream opened for OA metrics * @buf: destination buffer given by userspace * @count: the number of bytes userspace wants to read * @offset: (inout): the current position for writing into @buf * * Checks Gen 7 specific OA unit status registers and if necessary appends * corresponding status records for userspace (such as for a buffer full * condition) and then initiate appending any buffered OA reports. * * Updates @offset according to the number of bytes successfully copied into * the userspace buffer. * * Returns: zero on success or a negative error code
*/ staticint gen7_oa_read(struct i915_perf_stream *stream, char __user *buf,
size_t count,
size_t *offset)
{ struct intel_uncore *uncore = stream->uncore;
u32 oastatus1; int ret;
if (drm_WARN_ON(&uncore->i915->drm, !stream->oa_buffer.vaddr)) return -EIO;
/* XXX: On Haswell we don't have a safe way to clear oastatus1 * bits while the OA unit is enabled (while the tail pointer * may be updated asynchronously) so we ignore status bits * that have already been reported to userspace.
*/
oastatus1 &= ~stream->perf->gen7_latched_oastatus1;
/* We treat OABUFFER_OVERFLOW as a significant error: * * - The status can be interpreted to mean that the buffer is * currently full (with a higher precedence than OA_TAKEN() * which will start to report a near-empty buffer after an * overflow) but it's awkward that we can't clear the status * on Haswell, so without a reset we won't be able to catch * the state again. * * - Since it also implies the HW has started overwriting old * reports it may also affect our sanity checks for invalid * reports when copying to userspace that assume new reports * are being written to cleared memory. * * - In the future we may want to introduce a flight recorder * mode where the driver will automatically maintain a safe * guard band between head/tail, avoiding this overflow * condition, but we avoid the added driver complexity for * now.
*/ if (unlikely(oastatus1 & GEN7_OASTATUS1_OABUFFER_OVERFLOW)) {
ret = append_oa_status(stream, buf, count, offset,
DRM_I915_PERF_RECORD_OA_BUFFER_LOST); if (ret) return ret;
drm_dbg(&stream->perf->i915->drm, "OA buffer overflow (exponent = %d): force restart\n",
stream->period_exponent);
/** * i915_oa_wait_unlocked - handles blocking IO until OA data available * @stream: An i915-perf stream opened for OA metrics * * Called when userspace tries to read() from a blocking stream FD opened * for OA metrics. It waits until the hrtimer callback finds a non-empty * OA buffer and wakes us. * * Note: it's acceptable to have this return with some false positives * since any subsequent read handling will return -EAGAIN if there isn't * really data ready for userspace yet. * * Returns: zero on success or a negative error code
*/ staticint i915_oa_wait_unlocked(struct i915_perf_stream *stream)
{ /* We would wait indefinitely if periodic sampling is not enabled */ if (!stream->periodic) return -EIO;
/** * i915_oa_poll_wait - call poll_wait() for an OA stream poll() * @stream: An i915-perf stream opened for OA metrics * @file: An i915 perf stream file * @wait: poll() state table * * For handling userspace polling on an i915 perf stream opened for OA metrics, * this starts a poll_wait with the wait queue that our hrtimer callback wakes * when it sees data ready to read in the circular OA buffer.
*/ staticvoid i915_oa_poll_wait(struct i915_perf_stream *stream, struct file *file,
poll_table *wait)
{
poll_wait(file, &stream->poll_wq, wait);
}
/** * i915_oa_read - just calls through to &i915_oa_ops->read * @stream: An i915-perf stream opened for OA metrics * @buf: destination buffer given by userspace * @count: the number of bytes userspace wants to read * @offset: (inout): the current position for writing into @buf * * Updates @offset according to the number of bytes successfully copied into * the userspace buffer. * * Returns: zero on success or a negative error code
*/ staticint i915_oa_read(struct i915_perf_stream *stream, char __user *buf,
size_t count,
size_t *offset)
{ return stream->perf->ops.read(stream, buf, count, offset);
}
i915_gem_ww_ctx_init(&ww, true);
retry: /* * As the ID is the gtt offset of the context's vma we * pin the vma to ensure the ID remains fixed.
*/
err = intel_context_pin_ww(ce, &ww); if (err == -EDEADLK) {
err = i915_gem_ww_ctx_backoff(&ww); if (!err) goto retry;
}
i915_gem_ww_ctx_fini(&ww);
/* * For execlist mode of submission, pick an unused context id * 0 - (NUM_CONTEXT_TAG -1) are used by other contexts * XXX_MAX_CONTEXT_HW_ID is used by idle context * * For GuC mode of submission read context id from the upper dword of the * EXECLIST_STATUS register. Note that we read this value only once and expect * that the value stays fixed for the entire OA use case. There are cases where * GuC KMD implementation may deregister a context to reuse it's context id, but * we prevent that from happening to the OA context by pinning it.
*/ staticint gen12_get_render_context_id(struct i915_perf_stream *stream)
{
u32 ctx_id, mask; int ret;
if (intel_engine_uses_guc(stream->engine)) {
ret = gen12_guc_sw_ctx_id(stream->pinned_ctx, &ctx_id); if (ret) return ret;
if (drm_WARN_ON(&ce->engine->i915->drm, !state)) return U32_MAX;
for (offset = 0; offset < len; ) { if (IS_MI_LRI_CMD(state[offset])) { /* * We expect reg-value pairs in MI_LRI command, so * MI_LRI_LEN() should be even, if not, issue a warning.
*/
drm_WARN_ON(&ce->engine->i915->drm,
MI_LRI_LEN(state[offset]) & 0x1);
/** * oa_get_render_ctx_id - determine and hold ctx hw id * @stream: An i915-perf stream opened for OA metrics * * Determine the render context hw id, and ensure it remains fixed for the * lifetime of the stream. This ensures that we don't have to worry about * updating the context ID in OACONTROL on the fly. * * Returns: zero on success or a negative error code
*/ staticint oa_get_render_ctx_id(struct i915_perf_stream *stream)
{ struct intel_context *ce; int ret = 0;
ce = oa_pin_context(stream); if (IS_ERR(ce)) return PTR_ERR(ce);
if (engine_supports_mi_query(stream->engine) &&
HAS_LOGICAL_RING_CONTEXTS(stream->perf->i915)) { /* * We are enabling perf query here. If we don't find the context * offset here, just return an error.
*/
ret = set_oa_ctx_ctrl_offset(ce); if (ret) {
intel_context_unpin(ce);
drm_err(&stream->perf->i915->drm, "Enabling perf query failed for %s\n",
stream->engine->name); return ret;
}
}
switch (GRAPHICS_VER(ce->engine->i915)) { case 7: { /* * On Haswell we don't do any post processing of the reports * and don't need to use the mask.
*/
stream->specific_ctx_id = i915_ggtt_offset(ce->state);
stream->specific_ctx_id_mask = 0; break;
}
case 8: case 9: if (intel_engine_uses_guc(ce->engine)) { /* * When using GuC, the context descriptor we write in * i915 is read by GuC and rewritten before it's * actually written into the hardware. The LRCA is * what is put into the context id field of the * context descriptor by GuC. Because it's aligned to * a page, the lower 12bits are always at 0 and * dropped by GuC. They won't be part of the context * ID in the OA reports, so squash those lower bits.
*/
stream->specific_ctx_id = ce->lrc.lrca >> 12;
/* * GuC uses the top bit to signal proxy submission, so * ignore that bit.
*/
stream->specific_ctx_id_mask =
(1U << (GEN8_CTX_ID_WIDTH - 1)) - 1;
} else {
stream->specific_ctx_id_mask =
(1U << GEN8_CTX_ID_WIDTH) - 1;
stream->specific_ctx_id = stream->specific_ctx_id_mask;
} break;
case 11: case 12:
ret = gen12_get_render_context_id(stream); break;
drm_dbg(&stream->perf->i915->drm, "filtering on ctx_id=0x%x ctx_id_mask=0x%x\n",
stream->specific_ctx_id,
stream->specific_ctx_id_mask);
return ret;
}
/** * oa_put_render_ctx_id - counterpart to oa_get_render_ctx_id releases hold * @stream: An i915-perf stream opened for OA metrics * * In case anything needed doing to ensure the context HW ID would remain valid * for the lifetime of the stream, then that can be undone here.
*/ staticvoid oa_put_render_ctx_id(struct i915_perf_stream *stream)
{ struct intel_context *ce;
ce = fetch_and_zero(&stream->pinned_ctx); if (ce) {
ce->tag = 0; /* recomputed on next submission after parking */
intel_context_unpin(ce);
}
if (WARN_ON(stream != g->exclusive_stream)) return;
/* * Unset exclusive_stream first, it will be checked while disabling * the metric set on gen8+. * * See i915_oa_init_reg_state() and lrc_configure_all_contexts()
*/
WRITE_ONCE(g->exclusive_stream, NULL);
perf->ops.disable_metric_set(stream);
/* Pre-DevBDW: OABUFFER must be set with counters off, * before OASTATUS1, but after OASTATUS2
*/
intel_uncore_write(uncore, GEN7_OASTATUS2, /* head */
gtt_offset | GEN7_OASTATUS2_MEM_SELECT_GGTT);
stream->oa_buffer.head = 0;
/* On Haswell we have to track which OASTATUS1 flags we've * already seen since they can't be cleared while periodic * sampling is enabled.
*/
stream->perf->gen7_latched_oastatus1 = 0;
/* NB: although the OA buffer will initially be allocated * zeroed via shmfs (and so this memset is redundant when * first allocating), we may re-init the OA buffer, either * when re-enabling a stream or in error/reset paths. * * The reason we clear the buffer for each re-init is for the * sanity check in gen7_append_oa_reports() that looks at the * report-id field to make sure it's non-zero which relies on * the assumption that new reports are being written to zeroed * memory...
*/
memset(stream->oa_buffer.vaddr, 0, OA_BUFFER_SIZE);
}
/* * PRM says: * * "This MMIO must be set before the OATAILPTR * register and after the OAHEADPTR register. This is * to enable proper functionality of the overflow * bit."
*/
intel_uncore_write(uncore, GEN8_OABUFFER, gtt_offset |
OABUFFER_SIZE_16M | GEN8_OABUFFER_MEM_SELECT_GGTT);
intel_uncore_write(uncore, GEN8_OATAILPTR, gtt_offset & GEN8_OATAILPTR_MASK);
/* Mark that we need updated tail pointers to read from... */
stream->oa_buffer.tail = 0;
/* * Reset state used to recognise context switches, affecting which * reports we will forward to userspace while filtering for a single * context.
*/
stream->oa_buffer.last_ctx_id = INVALID_CTX_ID;
/* * NB: although the OA buffer will initially be allocated * zeroed via shmfs (and so this memset is redundant when * first allocating), we may re-init the OA buffer, either * when re-enabling a stream or in error/reset paths. * * The reason we clear the buffer for each re-init is for the * sanity check in gen8_append_oa_reports() that looks at the * reason field to make sure it's non-zero which relies on * the assumption that new reports are being written to zeroed * memory...
*/
memset(stream->oa_buffer.vaddr, 0, OA_BUFFER_SIZE);
}
/* * PRM says: * * "This MMIO must be set before the OATAILPTR * register and after the OAHEADPTR register. This is * to enable proper functionality of the overflow * bit."
*/
intel_uncore_write(uncore, __oa_regs(stream)->oa_buffer, gtt_offset |
OABUFFER_SIZE_16M | GEN8_OABUFFER_MEM_SELECT_GGTT);
intel_uncore_write(uncore, __oa_regs(stream)->oa_tail_ptr,
gtt_offset & GEN12_OAG_OATAILPTR_MASK);
/* Mark that we need updated tail pointers to read from... */
stream->oa_buffer.tail = 0;
/* * Reset state used to recognise context switches, affecting which * reports we will forward to userspace while filtering for a single * context.
*/
stream->oa_buffer.last_ctx_id = INVALID_CTX_ID;
/* * NB: although the OA buffer will initially be allocated * zeroed via shmfs (and so this memset is redundant when * first allocating), we may re-init the OA buffer, either * when re-enabling a stream or in error/reset paths. * * The reason we clear the buffer for each re-init is for the * sanity check in gen8_append_oa_reports() that looks at the * reason field to make sure it's non-zero which relies on * the assumption that new reports are being written to zeroed * memory...
*/
memset(stream->oa_buffer.vaddr, 0,
stream->oa_buffer.vma->size);
}
bo = i915_gem_object_create_shmem(stream->perf->i915, OA_BUFFER_SIZE); if (IS_ERR(bo)) {
drm_err(&i915->drm, "Failed to allocate OA buffer\n"); return PTR_ERR(bo);
}
/* * PreHSW required 512K alignment. * HSW and onwards, align to requested size of OA buffer.
*/
ret = i915_vma_pin(vma, 0, SZ_16M, PIN_GLOBAL | PIN_HIGH); if (ret) {
gt_err(gt, "Failed to pin OA buffer %d\n", ret); goto err_unref;
}
stream->oa_buffer.vma = vma;
stream->oa_buffer.vaddr =
i915_gem_object_pin_map_unlocked(bo, I915_MAP_WB); if (IS_ERR(stream->oa_buffer.vaddr)) {
ret = PTR_ERR(stream->oa_buffer.vaddr); goto err_unpin;
}
/* * gt->scratch was being used to save/restore the GPR registers, but on * MTL the scratch uses stolen lmem. An MI_SRM to this memory region * causes an engine hang. Instead allocate an additional page here to * save/restore GPR registers
*/
bo = i915_gem_object_create_internal(i915, 8192); if (IS_ERR(bo)) {
drm_err(&i915->drm, "Failed to allocate NOA wait batchbuffer\n"); return PTR_ERR(bo);
}
i915_gem_ww_ctx_init(&ww, true);
retry:
ret = i915_gem_object_lock(bo, &ww); if (ret) goto out_ww;
/* * We pin in GGTT because we jump into this buffer now because * multiple OA config BOs will have a jump to this address and it * needs to be fixed during the lifetime of the i915/perf stream.
*/
vma = i915_vma_instance(bo, >->ggtt->vm, NULL);
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.