summaryrefslogtreecommitdiff
path: root/drivers/edgetpu/edgetpu-telemetry.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/edgetpu/edgetpu-telemetry.c')
-rw-r--r--drivers/edgetpu/edgetpu-telemetry.c407
1 files changed, 94 insertions, 313 deletions
diff --git a/drivers/edgetpu/edgetpu-telemetry.c b/drivers/edgetpu/edgetpu-telemetry.c
index ce4739f..9376fba 100644
--- a/drivers/edgetpu/edgetpu-telemetry.c
+++ b/drivers/edgetpu/edgetpu-telemetry.c
@@ -5,15 +5,9 @@
* Copyright (C) 2019-2020 Google, Inc.
*/
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
+#include <linux/mm_types.h>
+
+#include <gcip/gcip-telemetry.h>
#include "edgetpu-internal.h"
#include "edgetpu-iremap-pool.h"
@@ -21,17 +15,13 @@
#include "edgetpu-telemetry.h"
#include "edgetpu.h"
-/* When log data arrives, recheck for more log data after this delay. */
-#define TELEMETRY_LOG_RECHECK_DELAY 200 /* ms */
-
-static struct edgetpu_telemetry *
-select_telemetry(struct edgetpu_telemetry_ctx *ctx,
- enum edgetpu_telemetry_type type)
+static struct gcip_telemetry *select_telemetry(struct edgetpu_telemetry_ctx *ctx,
+ enum gcip_telemetry_type type)
{
switch (type) {
- case EDGETPU_TELEMETRY_TRACE:
+ case GCIP_TELEMETRY_TRACE:
return &ctx->trace;
- case EDGETPU_TELEMETRY_LOG:
+ case GCIP_TELEMETRY_LOG:
return &ctx->log;
default:
WARN_ONCE(1, "Unrecognized EdgeTPU telemetry type: %d", type);
@@ -40,276 +30,19 @@ select_telemetry(struct edgetpu_telemetry_ctx *ctx,
}
}
-static int telemetry_kci(struct edgetpu_dev *etdev, struct edgetpu_telemetry *tel,
- int (*send_kci)(struct edgetpu_kci *, u64, u32))
-{
- int err;
-
- etdev_dbg(etdev, "Sending KCI %s", tel->name);
- err = send_kci(etdev->etkci, tel->coherent_mem.tpu_addr, tel->coherent_mem.size);
-
- if (err < 0) {
- etdev_err(etdev, "KCI %s failed :( - %d", tel->name, err);
- return err;
- }
-
- if (err > 0) {
- etdev_err(etdev, "KCI %s returned %d", tel->name, err);
- return -EBADMSG;
- }
- etdev_dbg(etdev, "KCI %s Succeeded :)", tel->name);
- return 0;
-}
-
-static int telemetry_set_event(struct edgetpu_dev *etdev,
- struct edgetpu_telemetry *tel, u32 eventfd)
-{
- struct eventfd_ctx *ctx;
- ulong flags;
-
- ctx = eventfd_ctx_fdget(eventfd);
- if (IS_ERR(ctx))
- return PTR_ERR(ctx);
-
- write_lock_irqsave(&tel->ctx_lock, flags);
- if (tel->ctx)
- eventfd_ctx_put(tel->ctx);
- tel->ctx = ctx;
- write_unlock_irqrestore(&tel->ctx_lock, flags);
-
- return 0;
-}
-
-static void telemetry_unset_event(struct edgetpu_dev *etdev,
- struct edgetpu_telemetry *tel)
-{
- ulong flags;
-
- write_lock_irqsave(&tel->ctx_lock, flags);
- if (tel->ctx)
- eventfd_ctx_put(tel->ctx);
- tel->ctx = NULL;
- write_unlock_irqrestore(&tel->ctx_lock, flags);
-
- return;
-}
-
-/* Copy data out of the log buffer with wrapping */
-static void copy_with_wrap(struct edgetpu_telemetry_header *header, void *dest,
- u32 length, u32 size, void *start)
-{
- const u32 wrap_bit = size + sizeof(*header);
- u32 remaining = 0;
- u32 head = header->head & (wrap_bit - 1);
-
- if (head + length < size) {
- memcpy(dest, start + head, length);
- header->head += length;
- } else {
- remaining = size - head;
- memcpy(dest, start + head, remaining);
- memcpy(dest + remaining, start, length - remaining);
- header->head = (header->head & wrap_bit) ^ wrap_bit;
- header->head |= length - remaining;
- }
-}
-
-/* Log messages from TPU CPU to dmesg */
-static void edgetpu_fw_log(struct edgetpu_telemetry *log)
-{
- struct edgetpu_dev *etdev = log->etdev;
- struct edgetpu_telemetry_header *header = log->header;
- struct edgetpu_log_entry_header entry;
- u8 *start;
- const size_t queue_size = log->coherent_mem.size - sizeof(*header);
- const size_t max_length = queue_size - sizeof(entry);
- char *buffer = kmalloc(max_length + 1, GFP_ATOMIC);
-
- if (!buffer) {
- header->head = header->tail;
- etdev_err_ratelimited(etdev, "failed to allocate log buffer");
- return;
- }
- start = (u8 *)header + sizeof(*header);
-
- while (header->head != header->tail) {
- copy_with_wrap(header, &entry, sizeof(entry), queue_size,
- start);
- if (entry.length == 0 || entry.length > max_length) {
- header->head = header->tail;
- etdev_err_ratelimited(etdev, "log queue is corrupted");
- break;
- }
- copy_with_wrap(header, buffer, entry.length, queue_size, start);
- buffer[entry.length] = 0;
-
- if (entry.code > EDGETPU_FW_DMESG_LOG_LEVEL)
- continue;
-
- switch (entry.code) {
- case EDGETPU_FW_LOG_LEVEL_VERBOSE:
- case EDGETPU_FW_LOG_LEVEL_DEBUG:
- etdev_dbg_ratelimited(etdev, "%s", buffer);
- break;
- case EDGETPU_FW_LOG_LEVEL_WARN:
- etdev_warn_ratelimited(etdev, "%s", buffer);
- break;
- case EDGETPU_FW_LOG_LEVEL_ERROR:
- etdev_err_ratelimited(etdev, "%s", buffer);
- break;
- case EDGETPU_FW_LOG_LEVEL_INFO:
- default:
- etdev_info_ratelimited(etdev, "%s", buffer);
- break;
- }
- }
- kfree(buffer);
-}
-
-/* Consumes the queue buffer. */
-static void edgetpu_fw_trace(struct edgetpu_telemetry *trace)
-{
- struct edgetpu_telemetry_header *header = trace->header;
-
- header->head = header->tail;
-}
-
-/* Worker for processing log/trace buffers. */
-
-static void telemetry_worker(struct work_struct *work)
-{
- struct edgetpu_telemetry *tel =
- container_of(work, struct edgetpu_telemetry, work);
- u32 prev_head;
- ulong flags;
-
- /*
- * Loop while telemetry enabled, there is data to be consumed,
- * and the previous iteration made progress. If another IRQ arrives
- * just after the last head != tail check we should get another worker
- * schedule.
- */
- do {
- spin_lock_irqsave(&tel->state_lock, flags);
- if (tel->state != EDGETPU_TELEMETRY_ENABLED) {
- spin_unlock_irqrestore(&tel->state_lock, flags);
- return;
- }
-
- prev_head = tel->header->head;
- if (tel->header->head != tel->header->tail) {
- read_lock(&tel->ctx_lock);
- if (tel->ctx)
- eventfd_signal(tel->ctx, 1);
- else
- tel->fallback_fn(tel);
- read_unlock(&tel->ctx_lock);
- }
-
- spin_unlock_irqrestore(&tel->state_lock, flags);
- msleep(TELEMETRY_LOG_RECHECK_DELAY);
- } while (tel->header->head != tel->header->tail &&
- tel->header->head != prev_head);
-}
-
-
-/* If the buffer queue is not empty, schedules worker. */
-static void telemetry_irq_handler(struct edgetpu_dev *etdev,
- struct edgetpu_telemetry *tel)
-{
- spin_lock(&tel->state_lock);
-
- if (tel->state == EDGETPU_TELEMETRY_ENABLED &&
- tel->header->head != tel->header->tail) {
- schedule_work(&tel->work);
- }
-
- spin_unlock(&tel->state_lock);
-}
-
-static void telemetry_mappings_show(struct edgetpu_telemetry *tel,
- struct seq_file *s)
-{
- seq_printf(s, " %#llx %lu %s %#llx %pad\n",
- tel->coherent_mem.tpu_addr,
- DIV_ROUND_UP(tel->coherent_mem.size, PAGE_SIZE), tel->name,
- tel->coherent_mem.host_addr, &tel->coherent_mem.dma_addr);
-}
-
-static void telemetry_inc_mmap_count(struct edgetpu_telemetry *tel, int dif)
-{
- mutex_lock(&tel->mmap_lock);
- tel->mmapped_count += dif;
- mutex_unlock(&tel->mmap_lock);
-}
-
-static int telemetry_mmap_buffer(struct edgetpu_dev *etdev,
- struct edgetpu_telemetry *tel,
- struct vm_area_struct *vma)
+static struct edgetpu_coherent_mem *select_telemetry_mem(struct edgetpu_telemetry_ctx *ctx,
+ enum gcip_telemetry_type type)
{
- int ret;
-
- mutex_lock(&tel->mmap_lock);
-
- if (!tel->mmapped_count) {
- ret = edgetpu_iremap_mmap(etdev, vma, &tel->coherent_mem);
-
- if (!ret) {
- tel->coherent_mem.host_addr = vma->vm_start;
- tel->mmapped_count = 1;
- }
- } else {
- ret = -EBUSY;
- etdev_warn(etdev, "%s is already mmapped %ld times", tel->name,
- tel->mmapped_count);
+ switch (type) {
+ case GCIP_TELEMETRY_TRACE:
+ return &ctx->trace_mem;
+ case GCIP_TELEMETRY_LOG:
+ return &ctx->log_mem;
+ default:
+ WARN_ONCE(1, "Unrecognized EdgeTPU telemetry type: %d", type);
+ /* return a valid object, don't crash the kernel */
+ return &ctx->log_mem;
}
-
- mutex_unlock(&tel->mmap_lock);
-
- return ret;
-}
-
-static int telemetry_init(struct edgetpu_dev *etdev, struct edgetpu_telemetry *tel,
- const char *name, struct edgetpu_coherent_mem *mem, const size_t size,
- void (*fallback)(struct edgetpu_telemetry *))
-{
- rwlock_init(&tel->ctx_lock);
- tel->name = name;
- tel->etdev = etdev;
- tel->coherent_mem = *mem;
-
- tel->header = (struct edgetpu_telemetry_header *)mem->vaddr;
- tel->header->head = 0;
- tel->header->size = 0;
- tel->header->tail = 0;
- tel->header->entries_dropped = 0;
-
- tel->ctx = NULL;
-
- spin_lock_init(&tel->state_lock);
- INIT_WORK(&tel->work, telemetry_worker);
- tel->fallback_fn = fallback;
- tel->state = EDGETPU_TELEMETRY_ENABLED;
- mutex_init(&tel->mmap_lock);
- tel->mmapped_count = 0;
-
- return 0;
-}
-
-static void telemetry_exit(struct edgetpu_dev *etdev,
- struct edgetpu_telemetry *tel)
-{
- ulong flags;
-
- spin_lock_irqsave(&tel->state_lock, flags);
- /* Prevent racing with the IRQ handler or worker */
- tel->state = EDGETPU_TELEMETRY_INVALID;
- spin_unlock_irqrestore(&tel->state_lock, flags);
- cancel_work_sync(&tel->work);
-
- if (tel->ctx)
- eventfd_ctx_put(tel->ctx);
- tel->ctx = NULL;
}
int edgetpu_telemetry_init(struct edgetpu_dev *etdev,
@@ -319,17 +52,22 @@ int edgetpu_telemetry_init(struct edgetpu_dev *etdev,
int ret, i;
for (i = 0; i < etdev->num_cores; i++) {
- ret = telemetry_init(etdev, &etdev->telemetry[i].log, "telemetry_log",
- log_mem ? &log_mem[i] : NULL,
- EDGETPU_TELEMETRY_LOG_BUFFER_SIZE, edgetpu_fw_log);
+ ret = gcip_telemetry_init(etdev->dev, &etdev->telemetry[i].log, "telemetry_log",
+ log_mem[i].vaddr, EDGETPU_TELEMETRY_LOG_BUFFER_SIZE,
+ gcip_telemetry_fw_log);
if (ret)
break;
+
+ etdev->telemetry[i].log_mem = log_mem[i];
+
#if IS_ENABLED(CONFIG_EDGETPU_TELEMETRY_TRACE)
- ret = telemetry_init(etdev, &etdev->telemetry[i].trace, "telemetry_trace",
- trace_mem ? &trace_mem[i] : NULL,
- EDGETPU_TELEMETRY_TRACE_BUFFER_SIZE, edgetpu_fw_trace);
+ ret = gcip_telemetry_init(etdev->dev, &etdev->telemetry[i].trace, "telemetry_trace",
+ trace_mem[i].vaddr, EDGETPU_TELEMETRY_TRACE_BUFFER_SIZE,
+ gcip_telemetry_fw_trace);
if (ret)
break;
+
+ etdev->telemetry[i].trace_mem = trace_mem[i];
#endif
}
@@ -345,23 +83,34 @@ void edgetpu_telemetry_exit(struct edgetpu_dev *etdev)
for (i = 0; i < etdev->num_cores; i++) {
#if IS_ENABLED(CONFIG_EDGETPU_TELEMETRY_TRACE)
- telemetry_exit(etdev, &etdev->telemetry[i].trace);
+ gcip_telemetry_exit(&etdev->telemetry[i].trace);
#endif
- telemetry_exit(etdev, &etdev->telemetry[i].log);
+ gcip_telemetry_exit(&etdev->telemetry[i].log);
}
}
int edgetpu_telemetry_kci(struct edgetpu_dev *etdev)
{
+ struct gcip_telemetry_kci_args log_args = {
+ .kci = etdev->etkci->kci,
+ .addr = etdev->telemetry[0].log_mem.tpu_addr,
+ .size = etdev->telemetry[0].log_mem.size,
+ };
+ struct gcip_telemetry_kci_args trace_args = {
+ .kci = etdev->etkci->kci,
+ .addr = etdev->telemetry[0].trace_mem.tpu_addr,
+ .size = etdev->telemetry[0].trace_mem.size,
+ };
int ret;
/* Core 0 will notify other cores. */
- ret = telemetry_kci(etdev, &etdev->telemetry[0].log, edgetpu_kci_map_log_buffer);
+ ret = gcip_telemetry_kci(&etdev->telemetry[0].log, edgetpu_kci_map_log_buffer, &log_args);
if (ret)
return ret;
#if IS_ENABLED(CONFIG_EDGETPU_TELEMETRY_TRACE)
- ret = telemetry_kci(etdev, &etdev->telemetry[0].trace, edgetpu_kci_map_trace_buffer);
+ ret = gcip_telemetry_kci(&etdev->telemetry[0].trace, edgetpu_kci_map_trace_buffer,
+ &trace_args);
if (ret)
return ret;
#endif
@@ -369,14 +118,14 @@ int edgetpu_telemetry_kci(struct edgetpu_dev *etdev)
return 0;
}
-int edgetpu_telemetry_set_event(struct edgetpu_dev *etdev,
- enum edgetpu_telemetry_type type, u32 eventfd)
+int edgetpu_telemetry_set_event(struct edgetpu_dev *etdev, enum gcip_telemetry_type type,
+ u32 eventfd)
{
int i, ret;
for (i = 0; i < etdev->num_cores; i++) {
- ret = telemetry_set_event(etdev, select_telemetry(&etdev->telemetry[i], type),
- eventfd);
+ ret = gcip_telemetry_set_event(select_telemetry(&etdev->telemetry[i], type),
+ eventfd);
if (ret) {
edgetpu_telemetry_unset_event(etdev, type);
return ret;
@@ -386,13 +135,12 @@ int edgetpu_telemetry_set_event(struct edgetpu_dev *etdev,
return 0;
}
-void edgetpu_telemetry_unset_event(struct edgetpu_dev *etdev,
- enum edgetpu_telemetry_type type)
+void edgetpu_telemetry_unset_event(struct edgetpu_dev *etdev, enum gcip_telemetry_type type)
{
int i;
for (i = 0; i < etdev->num_cores; i++)
- telemetry_unset_event(etdev, select_telemetry(&etdev->telemetry[i], type));
+ gcip_telemetry_unset_event(select_telemetry(&etdev->telemetry[i], type));
}
void edgetpu_telemetry_irq_handler(struct edgetpu_dev *etdev)
@@ -400,41 +148,74 @@ void edgetpu_telemetry_irq_handler(struct edgetpu_dev *etdev)
int i;
for (i = 0; i < etdev->num_cores; i++) {
- telemetry_irq_handler(etdev, &etdev->telemetry[i].log);
+ gcip_telemetry_irq_handler(&etdev->telemetry[i].log);
#if IS_ENABLED(CONFIG_EDGETPU_TELEMETRY_TRACE)
- telemetry_irq_handler(etdev, &etdev->telemetry[i].trace);
+ gcip_telemetry_irq_handler(&etdev->telemetry[i].trace);
#endif
}
}
+static void telemetry_mappings_show(struct gcip_telemetry *tel, struct edgetpu_coherent_mem *mem,
+ struct seq_file *s)
+{
+ seq_printf(s, " %#llx %lu %s %#llx %pad\n", mem->tpu_addr,
+ DIV_ROUND_UP(mem->size, PAGE_SIZE), tel->name, mem->host_addr, &mem->dma_addr);
+}
+
void edgetpu_telemetry_mappings_show(struct edgetpu_dev *etdev,
struct seq_file *s)
{
int i;
for (i = 0; i < etdev->num_cores; i++) {
- telemetry_mappings_show(&etdev->telemetry[i].log, s);
+ telemetry_mappings_show(&etdev->telemetry[i].log, &etdev->telemetry[i].log_mem, s);
#if IS_ENABLED(CONFIG_EDGETPU_TELEMETRY_TRACE)
- telemetry_mappings_show(&etdev->telemetry[i].trace, s);
+ telemetry_mappings_show(&etdev->telemetry[i].trace, &etdev->telemetry[i].trace_mem,
+ s);
#endif
}
}
-int edgetpu_mmap_telemetry_buffer(struct edgetpu_dev *etdev, enum edgetpu_telemetry_type type,
+struct edgetpu_telemetry_mmap_args {
+ struct edgetpu_dev *etdev;
+ struct edgetpu_coherent_mem *mem;
+ struct vm_area_struct *vma;
+};
+
+static int telemetry_mmap_buffer(void *args)
+{
+ struct edgetpu_telemetry_mmap_args *data = args;
+ int ret;
+
+ ret = edgetpu_iremap_mmap(data->etdev, data->vma, data->mem);
+
+ if (!ret)
+ data->mem->host_addr = data->vma->vm_start;
+
+ return ret;
+}
+
+int edgetpu_mmap_telemetry_buffer(struct edgetpu_dev *etdev, enum gcip_telemetry_type type,
struct vm_area_struct *vma, int core_id)
{
- return telemetry_mmap_buffer(etdev, select_telemetry(&etdev->telemetry[core_id], type),
- vma);
+ struct edgetpu_telemetry_mmap_args args = {
+ .etdev = etdev,
+ .mem = select_telemetry_mem(&etdev->telemetry[core_id], type),
+ .vma = vma,
+ };
+
+ return gcip_telemetry_mmap_buffer(select_telemetry(&etdev->telemetry[core_id], type),
+ telemetry_mmap_buffer, &args);
}
-void edgetpu_telemetry_inc_mmap_count(struct edgetpu_dev *etdev, enum edgetpu_telemetry_type type,
+void edgetpu_telemetry_inc_mmap_count(struct edgetpu_dev *etdev, enum gcip_telemetry_type type,
int core_id)
{
- telemetry_inc_mmap_count(select_telemetry(&etdev->telemetry[core_id], type), 1);
+ gcip_telemetry_inc_mmap_count(select_telemetry(&etdev->telemetry[core_id], type), 1);
}
-void edgetpu_telemetry_dec_mmap_count(struct edgetpu_dev *etdev, enum edgetpu_telemetry_type type,
+void edgetpu_telemetry_dec_mmap_count(struct edgetpu_dev *etdev, enum gcip_telemetry_type type,
int core_id)
{
- telemetry_inc_mmap_count(select_telemetry(&etdev->telemetry[core_id], type), -1);
+ gcip_telemetry_inc_mmap_count(select_telemetry(&etdev->telemetry[core_id], type), -1);
}