.. | .. |
---|
37 | 37 | |
---|
38 | 38 | static int tmc_etb_enable_hw(struct tmc_drvdata *drvdata) |
---|
39 | 39 | { |
---|
40 | | - int rc = coresight_claim_device(drvdata->base); |
---|
| 40 | + int rc = coresight_claim_device(drvdata->csdev); |
---|
41 | 41 | |
---|
42 | 42 | if (rc) |
---|
43 | 43 | return rc; |
---|
.. | .. |
---|
50 | 50 | { |
---|
51 | 51 | char *bufp; |
---|
52 | 52 | u32 read_data, lost; |
---|
53 | | - int i; |
---|
54 | 53 | |
---|
55 | 54 | /* Check if the buffer wrapped around. */ |
---|
56 | 55 | lost = readl_relaxed(drvdata->base + TMC_STS) & TMC_STS_FULL; |
---|
57 | 56 | bufp = drvdata->buf; |
---|
58 | 57 | drvdata->len = 0; |
---|
59 | 58 | while (1) { |
---|
60 | | - for (i = 0; i < drvdata->memwidth; i++) { |
---|
61 | | - read_data = readl_relaxed(drvdata->base + TMC_RRD); |
---|
62 | | - if (read_data == 0xFFFFFFFF) |
---|
63 | | - goto done; |
---|
64 | | - memcpy(bufp, &read_data, 4); |
---|
65 | | - bufp += 4; |
---|
66 | | - drvdata->len += 4; |
---|
67 | | - } |
---|
| 59 | + read_data = readl_relaxed(drvdata->base + TMC_RRD); |
---|
| 60 | + if (read_data == 0xFFFFFFFF) |
---|
| 61 | + break; |
---|
| 62 | + memcpy(bufp, &read_data, 4); |
---|
| 63 | + bufp += 4; |
---|
| 64 | + drvdata->len += 4; |
---|
68 | 65 | } |
---|
69 | | -done: |
---|
| 66 | + |
---|
70 | 67 | if (lost) |
---|
71 | 68 | coresight_insert_barrier_packet(drvdata->buf); |
---|
72 | 69 | return; |
---|
.. | .. |
---|
90 | 87 | |
---|
91 | 88 | static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) |
---|
92 | 89 | { |
---|
93 | | - coresight_disclaim_device(drvdata->base); |
---|
94 | 90 | __tmc_etb_disable_hw(drvdata); |
---|
| 91 | + coresight_disclaim_device(drvdata->csdev); |
---|
95 | 92 | } |
---|
96 | 93 | |
---|
97 | 94 | static void __tmc_etf_enable_hw(struct tmc_drvdata *drvdata) |
---|
.. | .. |
---|
112 | 109 | |
---|
113 | 110 | static int tmc_etf_enable_hw(struct tmc_drvdata *drvdata) |
---|
114 | 111 | { |
---|
115 | | - int rc = coresight_claim_device(drvdata->base); |
---|
| 112 | + int rc = coresight_claim_device(drvdata->csdev); |
---|
116 | 113 | |
---|
117 | 114 | if (rc) |
---|
118 | 115 | return rc; |
---|
.. | .. |
---|
123 | 120 | |
---|
124 | 121 | static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) |
---|
125 | 122 | { |
---|
| 123 | + struct coresight_device *csdev = drvdata->csdev; |
---|
| 124 | + |
---|
126 | 125 | CS_UNLOCK(drvdata->base); |
---|
127 | 126 | |
---|
128 | 127 | tmc_flush_and_stop(drvdata); |
---|
129 | 128 | tmc_disable_hw(drvdata); |
---|
130 | | - coresight_disclaim_device_unlocked(drvdata->base); |
---|
| 129 | + coresight_disclaim_device_unlocked(csdev); |
---|
131 | 130 | CS_LOCK(drvdata->base); |
---|
132 | 131 | } |
---|
133 | 132 | |
---|
.. | .. |
---|
226 | 225 | static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) |
---|
227 | 226 | { |
---|
228 | 227 | int ret = 0; |
---|
| 228 | + pid_t pid; |
---|
229 | 229 | unsigned long flags; |
---|
230 | 230 | struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
---|
231 | 231 | struct perf_output_handle *handle = data; |
---|
| 232 | + struct cs_buffers *buf = etm_perf_sink_config(handle); |
---|
232 | 233 | |
---|
233 | 234 | spin_lock_irqsave(&drvdata->spinlock, flags); |
---|
234 | 235 | do { |
---|
.. | .. |
---|
236 | 237 | if (drvdata->reading) |
---|
237 | 238 | break; |
---|
238 | 239 | /* |
---|
239 | | - * In Perf mode there can be only one writer per sink. There |
---|
240 | | - * is also no need to continue if the ETB/ETF is already |
---|
241 | | - * operated from sysFS. |
---|
| 240 | + * No need to continue if the ETB/ETF is already operated |
---|
| 241 | + * from sysFS. |
---|
242 | 242 | */ |
---|
243 | | - if (drvdata->mode != CS_MODE_DISABLED) |
---|
| 243 | + if (drvdata->mode == CS_MODE_SYSFS) { |
---|
| 244 | + ret = -EBUSY; |
---|
244 | 245 | break; |
---|
| 246 | + } |
---|
| 247 | + |
---|
| 248 | + /* Get a handle on the pid of the process to monitor */ |
---|
| 249 | + pid = buf->pid; |
---|
| 250 | + |
---|
| 251 | + if (drvdata->pid != -1 && drvdata->pid != pid) { |
---|
| 252 | + ret = -EBUSY; |
---|
| 253 | + break; |
---|
| 254 | + } |
---|
245 | 255 | |
---|
246 | 256 | ret = tmc_set_etf_buffer(csdev, handle); |
---|
247 | 257 | if (ret) |
---|
248 | 258 | break; |
---|
| 259 | + |
---|
| 260 | + /* |
---|
| 261 | + * No HW configuration is needed if the sink is already in |
---|
| 262 | + * use for this session. |
---|
| 263 | + */ |
---|
| 264 | + if (drvdata->pid == pid) { |
---|
| 265 | + atomic_inc(csdev->refcnt); |
---|
| 266 | + break; |
---|
| 267 | + } |
---|
| 268 | + |
---|
249 | 269 | ret = tmc_etb_enable_hw(drvdata); |
---|
250 | 270 | if (!ret) { |
---|
| 271 | + /* Associate with monitored process. */ |
---|
| 272 | + drvdata->pid = pid; |
---|
251 | 273 | drvdata->mode = CS_MODE_PERF; |
---|
252 | 274 | atomic_inc(csdev->refcnt); |
---|
253 | 275 | } |
---|
.. | .. |
---|
261 | 283 | u32 mode, void *data) |
---|
262 | 284 | { |
---|
263 | 285 | int ret; |
---|
264 | | - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
---|
265 | 286 | |
---|
266 | 287 | switch (mode) { |
---|
267 | 288 | case CS_MODE_SYSFS: |
---|
.. | .. |
---|
279 | 300 | if (ret) |
---|
280 | 301 | return ret; |
---|
281 | 302 | |
---|
282 | | - dev_dbg(drvdata->dev, "TMC-ETB/ETF enabled\n"); |
---|
| 303 | + dev_dbg(&csdev->dev, "TMC-ETB/ETF enabled\n"); |
---|
283 | 304 | return 0; |
---|
284 | 305 | } |
---|
285 | 306 | |
---|
.. | .. |
---|
303 | 324 | /* Complain if we (somehow) got out of sync */ |
---|
304 | 325 | WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); |
---|
305 | 326 | tmc_etb_disable_hw(drvdata); |
---|
| 327 | + /* Dissociate from monitored process. */ |
---|
| 328 | + drvdata->pid = -1; |
---|
306 | 329 | drvdata->mode = CS_MODE_DISABLED; |
---|
307 | 330 | |
---|
308 | 331 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
---|
309 | 332 | |
---|
310 | | - dev_dbg(drvdata->dev, "TMC-ETB/ETF disabled\n"); |
---|
| 333 | + dev_dbg(&csdev->dev, "TMC-ETB/ETF disabled\n"); |
---|
311 | 334 | return 0; |
---|
312 | 335 | } |
---|
313 | 336 | |
---|
.. | .. |
---|
337 | 360 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
---|
338 | 361 | |
---|
339 | 362 | if (first_enable) |
---|
340 | | - dev_dbg(drvdata->dev, "TMC-ETF enabled\n"); |
---|
| 363 | + dev_dbg(&csdev->dev, "TMC-ETF enabled\n"); |
---|
341 | 364 | return ret; |
---|
342 | 365 | } |
---|
343 | 366 | |
---|
.. | .. |
---|
362 | 385 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
---|
363 | 386 | |
---|
364 | 387 | if (last_disable) |
---|
365 | | - dev_dbg(drvdata->dev, "TMC-ETF disabled\n"); |
---|
| 388 | + dev_dbg(&csdev->dev, "TMC-ETF disabled\n"); |
---|
366 | 389 | } |
---|
367 | 390 | |
---|
368 | 391 | static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, |
---|
369 | 392 | struct perf_event *event, void **pages, |
---|
370 | 393 | int nr_pages, bool overwrite) |
---|
371 | 394 | { |
---|
372 | | - int node, cpu = event->cpu; |
---|
| 395 | + int node; |
---|
373 | 396 | struct cs_buffers *buf; |
---|
374 | 397 | |
---|
375 | | - node = (cpu == -1) ? NUMA_NO_NODE : cpu_to_node(cpu); |
---|
| 398 | + node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu); |
---|
376 | 399 | |
---|
377 | 400 | /* Allocate memory structure for interaction with Perf */ |
---|
378 | 401 | buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); |
---|
379 | 402 | if (!buf) |
---|
380 | 403 | return NULL; |
---|
381 | 404 | |
---|
| 405 | + buf->pid = task_pid_nr(event->owner); |
---|
382 | 406 | buf->snapshot = overwrite; |
---|
383 | 407 | buf->nr_pages = nr_pages; |
---|
384 | 408 | buf->data_pages = pages; |
---|
.. | .. |
---|
427 | 451 | u32 *buf_ptr; |
---|
428 | 452 | u64 read_ptr, write_ptr; |
---|
429 | 453 | u32 status; |
---|
430 | | - unsigned long offset, to_read, flags; |
---|
| 454 | + unsigned long offset, to_read = 0, flags; |
---|
431 | 455 | struct cs_buffers *buf = sink_config; |
---|
432 | 456 | struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
---|
433 | 457 | |
---|
.. | .. |
---|
439 | 463 | return 0; |
---|
440 | 464 | |
---|
441 | 465 | spin_lock_irqsave(&drvdata->spinlock, flags); |
---|
| 466 | + |
---|
| 467 | + /* Don't do anything if another tracer is using this sink */ |
---|
| 468 | + if (atomic_read(csdev->refcnt) != 1) |
---|
| 469 | + goto out; |
---|
| 470 | + |
---|
442 | 471 | CS_UNLOCK(drvdata->base); |
---|
443 | 472 | |
---|
444 | 473 | tmc_flush_and_stop(drvdata); |
---|
.. | .. |
---|
494 | 523 | |
---|
495 | 524 | cur = buf->cur; |
---|
496 | 525 | offset = buf->offset; |
---|
497 | | - barrier = barrier_pkt; |
---|
| 526 | + barrier = coresight_barrier_pkt; |
---|
498 | 527 | |
---|
499 | 528 | /* for every byte to read */ |
---|
500 | 529 | for (i = 0; i < to_read; i += 4) { |
---|
.. | .. |
---|
515 | 544 | } |
---|
516 | 545 | } |
---|
517 | 546 | |
---|
518 | | - /* In snapshot mode we have to update the head */ |
---|
519 | | - if (buf->snapshot) { |
---|
520 | | - handle->head = (cur * PAGE_SIZE) + offset; |
---|
521 | | - to_read = buf->nr_pages << PAGE_SHIFT; |
---|
522 | | - } |
---|
| 547 | + /* |
---|
| 548 | + * In snapshot mode we simply increment the head by the number of byte |
---|
| 549 | + * that were written. User space function cs_etm_find_snapshot() will |
---|
| 550 | + * figure out how many bytes to get from the AUX buffer based on the |
---|
| 551 | + * position of the head. |
---|
| 552 | + */ |
---|
| 553 | + if (buf->snapshot) |
---|
| 554 | + handle->head += to_read; |
---|
| 555 | + |
---|
523 | 556 | CS_LOCK(drvdata->base); |
---|
| 557 | +out: |
---|
524 | 558 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
---|
525 | 559 | |
---|
526 | 560 | return to_read; |
---|
.. | .. |
---|
566 | 600 | goto out; |
---|
567 | 601 | } |
---|
568 | 602 | |
---|
569 | | - /* There is no point in reading a TMC in HW FIFO mode */ |
---|
570 | | - mode = readl_relaxed(drvdata->base + TMC_MODE); |
---|
571 | | - if (mode != TMC_MODE_CIRCULAR_BUFFER) { |
---|
572 | | - ret = -EINVAL; |
---|
573 | | - goto out; |
---|
574 | | - } |
---|
575 | | - |
---|
576 | 603 | /* Don't interfere if operated from Perf */ |
---|
577 | 604 | if (drvdata->mode == CS_MODE_PERF) { |
---|
578 | 605 | ret = -EINVAL; |
---|
.. | .. |
---|
586 | 613 | } |
---|
587 | 614 | |
---|
588 | 615 | /* Disable the TMC if need be */ |
---|
589 | | - if (drvdata->mode == CS_MODE_SYSFS) |
---|
| 616 | + if (drvdata->mode == CS_MODE_SYSFS) { |
---|
| 617 | + /* There is no point in reading a TMC in HW FIFO mode */ |
---|
| 618 | + mode = readl_relaxed(drvdata->base + TMC_MODE); |
---|
| 619 | + if (mode != TMC_MODE_CIRCULAR_BUFFER) { |
---|
| 620 | + ret = -EINVAL; |
---|
| 621 | + goto out; |
---|
| 622 | + } |
---|
590 | 623 | __tmc_etb_disable_hw(drvdata); |
---|
| 624 | + } |
---|
591 | 625 | |
---|
592 | 626 | drvdata->reading = true; |
---|
593 | 627 | out: |
---|