Chris Wilson | b98bade | 2013-08-20 21:39:27 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright © 2013 Intel Corporation |
| 3 | * |
| 4 | * Permission is hereby granted, free of charge, to any person obtaining a |
| 5 | * copy of this software and associated documentation files (the "Software"), |
| 6 | * to deal in the Software without restriction, including without limitation |
| 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| 8 | * and/or sell copies of the Software, and to permit persons to whom the |
| 9 | * Software is furnished to do so, subject to the following conditions: |
| 10 | * |
| 11 | * The above copyright notice and this permission notice (including the next |
| 12 | * paragraph) shall be included in all copies or substantial portions of the |
| 13 | * Software. |
| 14 | * |
| 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 21 | * IN THE SOFTWARE. |
| 22 | * |
| 23 | */ |
| 24 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 25 | #include <linux/perf_event.h> |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 26 | #include <stdint.h> |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 27 | #include <stdlib.h> |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 28 | #include <stdio.h> |
| 29 | #include <string.h> |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 30 | #include <unistd.h> |
| 31 | #include <fcntl.h> |
| 32 | #include <errno.h> |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 33 | |
| 34 | #include "igfx.h" |
| 35 | #include "gpu-top.h" |
| 36 | |
| 37 | #define RING_TAIL 0x00 |
| 38 | #define RING_HEAD 0x04 |
| 39 | #define ADDR_MASK 0x001FFFFC |
| 40 | #define RING_CTL 0x0C |
| 41 | #define RING_WAIT (1<<11) |
| 42 | #define RING_WAIT_SEMAPHORE (1<<10) |
| 43 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 44 | #define __I915_PERF_RING(n) (4*n) |
| 45 | #define I915_PERF_RING_BUSY(n) (__I915_PERF_RING(n) + 0) |
| 46 | #define I915_PERF_RING_WAIT(n) (__I915_PERF_RING(n) + 1) |
| 47 | #define I915_PERF_RING_SEMA(n) (__I915_PERF_RING(n) + 2) |
| 48 | |
| 49 | static int |
| 50 | perf_event_open(struct perf_event_attr *attr, |
| 51 | pid_t pid, |
| 52 | int cpu, |
| 53 | int group_fd, |
| 54 | unsigned long flags) |
| 55 | { |
| 56 | #ifndef __NR_perf_event_open |
| 57 | #if defined(__i386__) |
| 58 | #define __NR_perf_event_open 336 |
| 59 | #elif defined(__x86_64__) |
| 60 | #define __NR_perf_event_open 298 |
| 61 | #else |
| 62 | #define __NR_perf_event_open 0 |
| 63 | #endif |
| 64 | #endif |
| 65 | attr->size = sizeof(*attr); |
| 66 | return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); |
| 67 | } |
| 68 | |
| 69 | static uint64_t i915_type_id(void) |
| 70 | { |
| 71 | char buf[1024]; |
| 72 | int fd, n; |
| 73 | |
| 74 | fd = open("/sys/bus/event_source/devices/i915/type", 0); |
Chris Wilson | a7b7420 | 2013-08-20 23:19:45 +0100 | [diff] [blame] | 75 | if (fd < 0) { |
| 76 | n = -1; |
| 77 | } else { |
| 78 | n = read(fd, buf, sizeof(buf)-1); |
| 79 | close(fd); |
| 80 | } |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 81 | if (n < 0) |
| 82 | return 0; |
| 83 | |
| 84 | buf[n] = '\0'; |
| 85 | return strtoull(buf, 0, 0); |
| 86 | } |
| 87 | |
| 88 | static int perf_i915_open(int config, int group) |
| 89 | { |
| 90 | struct perf_event_attr attr; |
| 91 | |
| 92 | memset(&attr, 0, sizeof (attr)); |
| 93 | |
| 94 | attr.type = i915_type_id(); |
| 95 | if (attr.type == 0) |
| 96 | return -ENOENT; |
| 97 | attr.config = config; |
| 98 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 99 | attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED; |
| 100 | if (group == -1) |
| 101 | attr.read_format |= PERF_FORMAT_GROUP; |
| 102 | |
| 103 | return perf_event_open(&attr, -1, 0, group, 0); |
| 104 | } |
| 105 | |
| 106 | static int perf_init(struct gpu_top *gt) |
| 107 | { |
| 108 | const char *names[] = { |
| 109 | "render", |
| 110 | "bitstream", |
| 111 | "bliter", |
| 112 | NULL, |
| 113 | }; |
| 114 | int n; |
| 115 | |
| 116 | gt->fd = perf_i915_open(I915_PERF_RING_BUSY(0), -1); |
| 117 | if (gt->fd < 0) |
| 118 | return -1; |
| 119 | |
| 120 | if (perf_i915_open(I915_PERF_RING_WAIT(0), gt->fd) >= 0) |
| 121 | gt->have_wait = 1; |
| 122 | |
| 123 | if (perf_i915_open(I915_PERF_RING_SEMA(0), gt->fd) >= 0) |
| 124 | gt->have_sema = 1; |
| 125 | |
| 126 | gt->ring[0].name = names[0]; |
| 127 | gt->num_rings = 1; |
| 128 | |
| 129 | for (n = 1; names[n]; n++) { |
| 130 | if (perf_i915_open(I915_PERF_RING_BUSY(n), gt->fd) >= 0) { |
| 131 | if (gt->have_wait && |
| 132 | perf_i915_open(I915_PERF_RING_WAIT(n), gt->fd) < 0) |
| 133 | return -1; |
| 134 | |
| 135 | if (gt->have_sema && |
| 136 | perf_i915_open(I915_PERF_RING_SEMA(n), gt->fd) < 0) |
| 137 | return -1; |
| 138 | |
| 139 | gt->ring[gt->num_rings++].name = names[n]; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | return 0; |
| 144 | } |
| 145 | |
| 146 | struct mmio_ring { |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 147 | int id; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 148 | uint32_t base; |
| 149 | void *mmio; |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 150 | int idle, wait, sema; |
| 151 | }; |
| 152 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 153 | static uint32_t mmio_ring_read(struct mmio_ring *ring, uint32_t reg) |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 154 | { |
Chris Wilson | 3e7dddc | 2013-08-21 09:16:25 +0100 | [diff] [blame] | 155 | return igfx_read(ring->mmio, ring->base + reg); |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 156 | } |
| 157 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 158 | static void mmio_ring_init(struct mmio_ring *ring, void *mmio) |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 159 | { |
| 160 | uint32_t ctl; |
| 161 | |
Chris Wilson | 3e7dddc | 2013-08-21 09:16:25 +0100 | [diff] [blame] | 162 | ring->mmio = mmio; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 163 | |
| 164 | ctl = mmio_ring_read(ring, RING_CTL); |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 165 | if ((ctl & 1) == 0) |
| 166 | ring->id = -1; |
| 167 | } |
| 168 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 169 | static void mmio_ring_reset(struct mmio_ring *ring) |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 170 | { |
| 171 | ring->idle = 0; |
| 172 | ring->wait = 0; |
| 173 | ring->sema = 0; |
| 174 | } |
| 175 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 176 | static void mmio_ring_sample(struct mmio_ring *ring) |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 177 | { |
| 178 | uint32_t head, tail, ctl; |
| 179 | |
| 180 | if (ring->id == -1) |
| 181 | return; |
| 182 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 183 | head = mmio_ring_read(ring, RING_HEAD) & ADDR_MASK; |
| 184 | tail = mmio_ring_read(ring, RING_TAIL) & ADDR_MASK; |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 185 | ring->idle += head == tail; |
| 186 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 187 | ctl = mmio_ring_read(ring, RING_CTL); |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 188 | ring->wait += !!(ctl & RING_WAIT); |
| 189 | ring->sema += !!(ctl & RING_WAIT_SEMAPHORE); |
| 190 | } |
| 191 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 192 | static void mmio_ring_emit(struct mmio_ring *ring, int samples, union gpu_top_payload *payload) |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 193 | { |
| 194 | if (ring->id == -1) |
| 195 | return; |
| 196 | |
| 197 | payload[ring->id].u.busy = 100 - 100 * ring->idle / samples; |
| 198 | payload[ring->id].u.wait = 100 * ring->wait / samples; |
| 199 | payload[ring->id].u.sema = 100 * ring->sema / samples; |
| 200 | } |
| 201 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 202 | static void mmio_init(struct gpu_top *gt) |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 203 | { |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 204 | struct mmio_ring render_ring = { |
| 205 | .base = 0x2030, |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 206 | .id = 0, |
| 207 | }, bsd_ring = { |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 208 | .base = 0x4030, |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 209 | .id = 1, |
| 210 | }, bsd6_ring = { |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 211 | .base = 0x12030, |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 212 | .id = 1, |
| 213 | }, blt_ring = { |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 214 | .base = 0x22030, |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 215 | .id = 2, |
| 216 | }; |
| 217 | const struct igfx_info *info; |
| 218 | struct pci_device *igfx; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 219 | void *mmio; |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 220 | int fd[2], i; |
| 221 | |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 222 | igfx = igfx_get(); |
| 223 | if (!igfx) |
| 224 | return; |
| 225 | |
| 226 | if (pipe(fd) < 0) |
| 227 | return; |
| 228 | |
| 229 | info = igfx_get_info(igfx); |
| 230 | |
| 231 | switch (fork()) { |
| 232 | case -1: return; |
| 233 | default: |
| 234 | fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_NONBLOCK); |
| 235 | gt->fd = fd[0]; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 236 | gt->type = MMIO; |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 237 | gt->ring[0].name = "render"; |
| 238 | gt->num_rings = 1; |
| 239 | if (info->gen >= 040) { |
| 240 | gt->ring[1].name = "bitstream"; |
| 241 | gt->num_rings++; |
| 242 | } |
| 243 | if (info->gen >= 060) { |
| 244 | gt->ring[2].name = "blt"; |
| 245 | gt->num_rings++; |
| 246 | } |
| 247 | close(fd[1]); |
| 248 | return; |
| 249 | case 0: |
| 250 | close(fd[0]); |
| 251 | break; |
| 252 | } |
| 253 | |
| 254 | mmio = igfx_get_mmio(igfx); |
Chris Wilson | 3e7dddc | 2013-08-21 09:16:25 +0100 | [diff] [blame] | 255 | if (mmio == NULL) |
| 256 | exit(127); |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 257 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 258 | mmio_ring_init(&render_ring, mmio); |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 259 | if (info->gen >= 060) { |
Chris Wilson | 3e7dddc | 2013-08-21 09:16:25 +0100 | [diff] [blame] | 260 | bsd_ring = bsd6_ring; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 261 | mmio_ring_init(&blt_ring, mmio); |
Chris Wilson | 3e7dddc | 2013-08-21 09:16:25 +0100 | [diff] [blame] | 262 | } |
| 263 | if (info->gen >= 040) { |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 264 | mmio_ring_init(&bsd_ring, mmio); |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 265 | } |
| 266 | |
| 267 | for (;;) { |
| 268 | union gpu_top_payload payload[MAX_RINGS]; |
| 269 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 270 | mmio_ring_reset(&render_ring); |
| 271 | mmio_ring_reset(&bsd_ring); |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 272 | mmio_ring_reset(&blt_ring); |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 273 | |
| 274 | for (i = 0; i < 1000; i++) { |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 275 | mmio_ring_sample(&render_ring); |
| 276 | mmio_ring_sample(&bsd_ring); |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 277 | mmio_ring_sample(&blt_ring); |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 278 | usleep(1000); |
| 279 | } |
| 280 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 281 | mmio_ring_emit(&render_ring, 1000, payload); |
| 282 | mmio_ring_emit(&bsd_ring, 1000, payload); |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 283 | mmio_ring_emit(&blt_ring, 1000, payload); |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 284 | |
| 285 | write(fd[1], payload, sizeof(payload)); |
| 286 | } |
| 287 | } |
| 288 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 289 | void gpu_top_init(struct gpu_top *gt) |
| 290 | { |
| 291 | memset(gt, 0, sizeof(*gt)); |
| 292 | gt->fd = -1; |
| 293 | |
| 294 | if (perf_init(gt) == 0) |
| 295 | return; |
| 296 | |
| 297 | mmio_init(gt); |
| 298 | } |
| 299 | |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 300 | int gpu_top_update(struct gpu_top *gt) |
| 301 | { |
| 302 | uint32_t data[1024]; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 303 | int update, len; |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 304 | |
| 305 | if (gt->fd < 0) |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 306 | return 0; |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 307 | |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 308 | if (gt->type == PERF) { |
| 309 | struct gpu_top_stat *s = >->stat[gt->count++&1]; |
| 310 | struct gpu_top_stat *d = >->stat[gt->count&1]; |
| 311 | uint64_t *sample, d_time; |
Chris Wilson | a7b7420 | 2013-08-20 23:19:45 +0100 | [diff] [blame] | 312 | int n, m; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 313 | |
| 314 | len = read(gt->fd, data, sizeof(data)); |
| 315 | if (len < 0) |
| 316 | return 0; |
| 317 | |
| 318 | sample = (uint64_t *)data + 1; |
| 319 | |
| 320 | s->time = *sample++; |
Chris Wilson | a7b7420 | 2013-08-20 23:19:45 +0100 | [diff] [blame] | 321 | for (n = m = 0; n < gt->num_rings; n++) { |
| 322 | s->busy[n] = sample[m++]; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 323 | if (gt->have_wait) |
Chris Wilson | a7b7420 | 2013-08-20 23:19:45 +0100 | [diff] [blame] | 324 | s->wait[n] = sample[m++]; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 325 | if (gt->have_sema) |
Chris Wilson | a7b7420 | 2013-08-20 23:19:45 +0100 | [diff] [blame] | 326 | s->sema[n] = sample[m++]; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 327 | } |
| 328 | |
| 329 | if (gt->count == 1) |
| 330 | return 0; |
| 331 | |
| 332 | d_time = s->time - d->time; |
| 333 | for (n = 0; n < gt->num_rings; n++) { |
| 334 | gt->ring[n].u.u.busy = 100 * (s->busy[n] - d->busy[n]) / d_time; |
| 335 | if (gt->have_wait) |
| 336 | gt->ring[n].u.u.wait = 100 * (s->wait[n] - d->wait[n]) / d_time; |
| 337 | if (gt->have_sema) |
| 338 | gt->ring[n].u.u.sema = 100 * (s->sema[n] - d->sema[n]) / d_time; |
| 339 | } |
| 340 | |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 341 | update = 1; |
Chris Wilson | 2e482a3 | 2013-08-20 19:11:44 +0100 | [diff] [blame] | 342 | } else { |
| 343 | while ((len = read(gt->fd, data, sizeof(data))) > 0) { |
| 344 | uint32_t *ptr = &data[len/sizeof(uint32_t) - MAX_RINGS]; |
| 345 | gt->ring[0].u.payload = ptr[0]; |
| 346 | gt->ring[1].u.payload = ptr[1]; |
| 347 | gt->ring[2].u.payload = ptr[2]; |
| 348 | gt->ring[3].u.payload = ptr[3]; |
| 349 | update = 1; |
| 350 | } |
Chris Wilson | f9a50de | 2013-08-17 11:12:07 +0100 | [diff] [blame] | 351 | } |
| 352 | |
| 353 | return update; |
| 354 | } |