summaryrefslogtreecommitdiff
path: root/gxp-telemetry.c
diff options
context:
space:
mode:
Diffstat (limited to 'gxp-telemetry.c')
-rw-r--r--gxp-telemetry.c266
1 files changed, 242 insertions, 24 deletions
diff --git a/gxp-telemetry.c b/gxp-telemetry.c
index 4e97ec1..0afc6de 100644
--- a/gxp-telemetry.c
+++ b/gxp-telemetry.c
@@ -6,21 +6,68 @@
*/
#include <linux/slab.h>
+#include <linux/wait.h>
#include "gxp-config.h"
#include "gxp-dma.h"
+#include "gxp-firmware.h"
#include "gxp-firmware-data.h"
+#include "gxp-host-device-structs.h"
+#include "gxp-notification.h"
#include "gxp-telemetry.h"
+static inline bool is_core_telemetry_enabled(struct gxp_dev *gxp, uint core,
+ u8 type)
+{
+ u32 device_status =
+ gxp_fw_data_get_telemetry_device_status(gxp, core, type);
+
+ return device_status & GXP_TELEMETRY_DEVICE_STATUS_ENABLED;
+}
+
+static void telemetry_status_notification_work(struct work_struct *work)
+{
+ struct gxp_telemetry_work *telem_work =
+ container_of(work, struct gxp_telemetry_work, work);
+ struct gxp_dev *gxp = telem_work->gxp;
+ uint core = telem_work->core;
+ struct gxp_telemetry_manager *mgr = telem_work->gxp->telemetry_mgr;
+
+ /* Wake any threads waiting on an telemetry disable ACK */
+ wake_up(&mgr->waitq);
+
+ /* Signal the appropriate eventfd for any active telemetry types */
+ mutex_lock(&mgr->lock);
+
+ if (is_core_telemetry_enabled(gxp, core, GXP_TELEMETRY_TYPE_LOGGING) &&
+ mgr->logging_efd)
+ eventfd_signal(mgr->logging_efd, 1);
+
+ if (is_core_telemetry_enabled(gxp, core, GXP_TELEMETRY_TYPE_TRACING) &&
+ mgr->tracing_efd)
+ eventfd_signal(mgr->tracing_efd, 1);
+
+ mutex_unlock(&mgr->lock);
+}
+
int gxp_telemetry_init(struct gxp_dev *gxp)
{
struct gxp_telemetry_manager *mgr;
+ uint i;
mgr = devm_kzalloc(gxp->dev, sizeof(*mgr), GFP_KERNEL);
if (!mgr)
return -ENOMEM;
mutex_init(&mgr->lock);
+ for (i = 0; i < GXP_NUM_CORES; i++) {
+ INIT_WORK(&mgr->notification_works[i].work,
+ telemetry_status_notification_work);
+ mgr->notification_works[i].gxp = gxp;
+ mgr->notification_works[i].core = i;
+
+ }
+ init_waitqueue_head(&mgr->waitq);
gxp->telemetry_mgr = mgr;
@@ -49,6 +96,13 @@ static void gxp_telemetry_vma_open(struct vm_area_struct *vma)
mutex_unlock(&gxp->telemetry_mgr->lock);
}
+/*
+ * Forward declaration of telemetry_disable_locked() so that
+ * gxp_telemetry_vma_close() can invoke the locked version without having to
+ * release `telemetry_mgr->lock` and calling gxp_telemetry_disable().
+ */
+static int telemetry_disable_locked(struct gxp_dev *gxp, u8 type);
+
static void gxp_telemetry_vma_close(struct vm_area_struct *vma)
{
struct gxp_dev *gxp;
@@ -63,8 +117,8 @@ static void gxp_telemetry_vma_close(struct vm_area_struct *vma)
mutex_lock(&gxp->telemetry_mgr->lock);
if (refcount_dec_and_test(&data->ref_count)) {
- if (data->enabled)
- gxp_telemetry_disable(gxp, type);
+ if (data->host_status & GXP_TELEMETRY_HOST_STATUS_ENABLED)
+ telemetry_disable_locked(gxp, type);
for (i = 0; i < GXP_NUM_CORES; i++)
gxp_dma_free_coherent(gxp, BIT(i), data->size,
@@ -156,7 +210,6 @@ static struct buffer_data *allocate_telemetry_buffers(struct gxp_dev *gxp,
}
data->size = size;
refcount_set(&data->ref_count, 1);
- data->enabled = false;
return data;
@@ -304,6 +357,7 @@ int gxp_telemetry_enable(struct gxp_dev *gxp, u8 type)
{
struct buffer_data *data;
int ret = 0;
+ uint core;
mutex_lock(&gxp->telemetry_mgr->lock);
@@ -325,13 +379,18 @@ int gxp_telemetry_enable(struct gxp_dev *gxp, u8 type)
}
/* Populate the buffer fields in firmware-data */
- gxp_fw_data_set_telemetry_descriptors(
- gxp, type, (u32 *)data->buffer_daddrs, data->size);
+ data->host_status |= GXP_TELEMETRY_HOST_STATUS_ENABLED;
+ gxp_fw_data_set_telemetry_descriptors(gxp, type, data->host_status,
+ data->buffer_daddrs, data->size);
- /* TODO(b/202937192) To be done in a future CL */
/* Notify any running cores that firmware-data was updated */
-
- data->enabled = true;
+ down_read(&gxp->vd_semaphore);
+ for (core = 0; core < GXP_NUM_CORES; core++) {
+ if (gxp_is_fw_running(gxp, core))
+ gxp_notification_send(gxp, core,
+ CORE_NOTIF_TELEMETRY_STATUS);
+ }
+ up_read(&gxp->vd_semaphore);
out:
mutex_unlock(&gxp->telemetry_mgr->lock);
@@ -339,13 +398,82 @@ out:
return ret;
}
-int gxp_telemetry_disable(struct gxp_dev *gxp, u8 type)
+/**
+ * notify_core_and_wait_for_disable() - Notify a core that telemetry state has
+ * been changed by the host and wait for
+ * the core to stop using telemetry.
+ * @gxp: The GXP device telemetry is changing for
+ * @core: The core in @gxp to notify of the telemetry state change
+ * @type: Either `GXP_TELEMETRY_TYPE_LOGGING` or `GXP_TELEMETRY_TYPE_TRACING`
+ *
+ * Caller must hold @gxp's virtual device lock
+ *
+ * Return:
+ * * 0 - Firmware on @core is no longer using telemetry of @type
+ * * -ENXIO - Firmware on @core is unresponsive
+ */
+static int notify_core_and_wait_for_disable(struct gxp_dev *gxp, uint core,
+ u8 type)
+{
+ uint retries_left = 50;
+
+ gxp_notification_send(gxp, core, CORE_NOTIF_TELEMETRY_STATUS);
+
+ /* Wait for ACK from firmware */
+ while (is_core_telemetry_enabled(gxp, core, type) &&
+ gxp_is_fw_running(gxp, core) && retries_left) {
+ /* Release vd_semaphore while waiting */
+ up_read(&gxp->vd_semaphore);
+
+ /*
+ * The VD lock must be held to check if firmware is running, so
+ * the wait condition is only whether the firmware data has been
+ * updated to show the core disabling telemetry.
+ *
+ * If a core does stop running firmware while this function is
+ * asleep, it will be seen at the next timeout.
+ */
+ wait_event_timeout(gxp->telemetry_mgr->waitq,
+ !is_core_telemetry_enabled(gxp, core, type),
+ msecs_to_jiffies(10));
+ retries_left--;
+
+ down_read(&gxp->vd_semaphore);
+ }
+
+ /*
+ * If firmware has stopped running altogether, that is sufficient to be
+ * considered disabled. If firmware is started on this core again, it
+ * is responsible for clearing its status.
+ */
+ if (unlikely(is_core_telemetry_enabled(gxp, core, type) &&
+ gxp_is_fw_running(gxp, core)))
+ return -ENXIO;
+
+ return 0;
+}
+
+/**
+ * telemetry_disable_locked() - Helper function to break out the actual
+ * process of disabling telemetry so that it
+ * can be invoked by internal functions that are
+ * already holding the telemetry lock.
+ * @gxp: The GXP device to disable either logging or tracing for
+ * @type: Either `GXP_TELEMETRY_TYPE_LOGGING` or `GXP_TELEMETRY_TYPE_TRACING`
+ *
+ * Caller must hold `telemetry_mgr->lock`.
+ *
+ * Return:
+ * * 0 - Success
+ * * -EINVAL - The @type provided is not valid
+ * * -ENXIO - Buffers for @type have not been created/mapped yet
+ */
+static int telemetry_disable_locked(struct gxp_dev *gxp, u8 type)
{
struct buffer_data *data;
int ret = 0;
- u32 null_daddrs[GXP_NUM_CORES] = {0};
-
- mutex_lock(&gxp->telemetry_mgr->lock);
+ dma_addr_t null_daddrs[GXP_NUM_CORES] = {0};
+ uint core;
/* Cleanup telemetry manager's book-keeping */
switch (type) {
@@ -356,29 +484,119 @@ int gxp_telemetry_disable(struct gxp_dev *gxp, u8 type)
data = gxp->telemetry_mgr->tracing_buff_data;
break;
default:
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
- if (!data) {
- ret = -ENXIO;
- goto out;
- }
+ if (!data)
+ return -ENXIO;
- if (!data->enabled)
- goto out;
+ if (!(data->host_status & GXP_TELEMETRY_HOST_STATUS_ENABLED))
+ return 0;
/* Clear the log buffer fields in firmware-data */
- gxp_fw_data_set_telemetry_descriptors(gxp, type, null_daddrs, 0);
+ data->host_status &= ~GXP_TELEMETRY_HOST_STATUS_ENABLED;
+ gxp_fw_data_set_telemetry_descriptors(gxp, type, data->host_status,
+ null_daddrs, 0);
- /* TODO(b/202937192) To be done in a future CL */
/* Notify any running cores that firmware-data was updated */
- /* Wait for ACK from firmware */
+ down_read(&gxp->vd_semaphore);
+ for (core = 0; core < GXP_NUM_CORES; core++) {
+ if (gxp_is_fw_running(gxp, core)) {
+ ret = notify_core_and_wait_for_disable(gxp, core, type);
+ if (ret)
+ dev_warn(
+ gxp->dev,
+ "%s: core%u failed to disable telemetry (type=%u, ret=%d)\n",
+ __func__, core, type, ret);
+ }
+ }
+ up_read(&gxp->vd_semaphore);
+
+ return 0;
+}
+
+int gxp_telemetry_disable(struct gxp_dev *gxp, u8 type)
+{
+ int ret;
+
+ mutex_lock(&gxp->telemetry_mgr->lock);
+
+ ret = telemetry_disable_locked(gxp, type);
+
+ mutex_unlock(&gxp->telemetry_mgr->lock);
+
+ return ret;
+}
+
+int gxp_telemetry_register_eventfd(struct gxp_dev *gxp, u8 type, int fd)
+{
+ struct eventfd_ctx *new_ctx;
+ struct eventfd_ctx **ctx_to_set = NULL;
+ int ret = 0;
+
+ new_ctx = eventfd_ctx_fdget(fd);
+ if (IS_ERR(new_ctx))
+ return PTR_ERR(new_ctx);
- data->enabled = false;
+ mutex_lock(&gxp->telemetry_mgr->lock);
+
+ switch (type) {
+ case GXP_TELEMETRY_TYPE_LOGGING:
+ ctx_to_set = &gxp->telemetry_mgr->logging_efd;
+ break;
+ case GXP_TELEMETRY_TYPE_TRACING:
+ ctx_to_set = &gxp->telemetry_mgr->tracing_efd;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (*ctx_to_set) {
+ dev_warn(gxp->dev,
+ "Replacing existing telemetry eventfd (type=%u)\n",
+ type);
+ eventfd_ctx_put(*ctx_to_set);
+ }
+
+ *ctx_to_set = new_ctx;
out:
mutex_unlock(&gxp->telemetry_mgr->lock);
+ return ret;
+}
+
+int gxp_telemetry_unregister_eventfd(struct gxp_dev *gxp, u8 type)
+{
+ int ret = 0;
+
+ mutex_lock(&gxp->telemetry_mgr->lock);
+
+ switch (type) {
+ case GXP_TELEMETRY_TYPE_LOGGING:
+ eventfd_ctx_put(gxp->telemetry_mgr->logging_efd);
+ gxp->telemetry_mgr->logging_efd = NULL;
+ break;
+ case GXP_TELEMETRY_TYPE_TRACING:
+ eventfd_ctx_put(gxp->telemetry_mgr->tracing_efd);
+ gxp->telemetry_mgr->tracing_efd = NULL;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&gxp->telemetry_mgr->lock);
return ret;
}
+
+struct work_struct *gxp_telemetry_get_notification_handler(struct gxp_dev *gxp,
+ uint core)
+{
+ struct gxp_telemetry_manager *mgr = gxp->telemetry_mgr;
+
+ if (!mgr || core >= GXP_NUM_CORES)
+ return NULL;
+
+ return &mgr->notification_works[core].work;
+}