// SPDX-License-Identifier: GPL-2.0-only /* * GXP core telemetry support * * Copyright (C) 2021-2022 Google LLC */ #include #include #include #include "gxp-config.h" #include "gxp-core-telemetry.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-vd.h" static uint gxp_core_telemetry_buffer_size = CORE_TELEMETRY_DEFAULT_BUFFER_SIZE; module_param_named(core_telemetry_buffer_size, gxp_core_telemetry_buffer_size, uint, 0660); static inline bool is_telemetry_enabled(struct gxp_dev *gxp, uint core, u8 type) { u32 device_status = gxp_fw_data_get_core_telemetry_device_status(gxp, core, type); return device_status & GXP_CORE_TELEMETRY_DEVICE_STATUS_ENABLED; } void gxp_core_telemetry_status_notify(struct gxp_dev *gxp, uint core) { struct gxp_core_telemetry_manager *mgr = gxp->core_telemetry_mgr; /* Wake any threads waiting on a core telemetry disable ACK */ wake_up(&mgr->waitq); /* Signal the appropriate eventfd for any active core telemetry types */ mutex_lock(&mgr->lock); if (is_telemetry_enabled(gxp, core, GXP_TELEMETRY_TYPE_LOGGING) && mgr->logging_efd) eventfd_signal(mgr->logging_efd, 1); if (is_telemetry_enabled(gxp, core, GXP_TELEMETRY_TYPE_TRACING) && mgr->tracing_efd) eventfd_signal(mgr->tracing_efd, 1); mutex_unlock(&mgr->lock); } static void telemetry_status_notification_work(struct work_struct *work) { struct gxp_core_telemetry_work *telem_work = container_of(work, struct gxp_core_telemetry_work, work); struct gxp_dev *gxp = telem_work->gxp; uint core = telem_work->core; gxp_core_telemetry_status_notify(gxp, core); } static struct buffer_data *allocate_telemetry_buffers(struct gxp_dev *gxp, size_t size); static void free_telemetry_buffers(struct gxp_dev *gxp, struct buffer_data *data); /** * enable_telemetry_buffers() - enable the telemetry buffers from host. * * @gxp: The GXP device the buffers were allocated for. * @data: The data describing a set of core telemetry buffers to be enabled. * @type: Either `GXP_TELEMETRY_TYPE_LOGGING` or `GXP_TELEMETRY_TYPE_TRACING`. * * Return: * * 0 - Success * * otherwise - Error returned by `gxp_fw_data_set_core_telemetry_descriptors()` */ static int enable_telemetry_buffers(struct gxp_dev *gxp, struct buffer_data *data, u8 type) { int i, ret; /* Initialize the per core telemetry buffers header with magic code. */ for (i = 0; i < GXP_NUM_CORES; i++) { /* * First 64 bytes of per core telemetry buffers are reserved * for buffer metadata header. We don't need to explicitly * reset the header fields as during buffer allocation the * entire buffer is zeroed out. First 4 bytes of buffer * metadata header are reserved for valid_magic field. */ *((uint *)data->buffers[i].vaddr) = GXP_TELEMETRY_BUFFER_VALID_MAGIC_CODE; } data->host_status |= GXP_CORE_TELEMETRY_HOST_STATUS_ENABLED; ret = gxp_fw_data_set_core_telemetry_descriptors( gxp, type, data->host_status, data->buffers, data->size); if (ret) { dev_err(gxp->dev, "setting telemetry buffers in scratchpad region failed (ret=%d).", ret); return ret; } data->is_enabled = true; return 0; } int gxp_core_telemetry_init(struct gxp_dev *gxp) { struct gxp_core_telemetry_manager *mgr; struct buffer_data *log_buff_data, *trace_buff_data; int i, ret; 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->core_telemetry_mgr = mgr; gxp_core_telemetry_buffer_size = ALIGN(gxp_core_telemetry_buffer_size, GXP_CORE_TELEMETRY_BUFFER_UNIT_SIZE); if ((gxp_core_telemetry_buffer_size < CORE_TELEMETRY_DEFAULT_BUFFER_SIZE) || (gxp_core_telemetry_buffer_size > CORE_TELEMETRY_MAX_BUFFER_SIZE)) { dev_warn(gxp->dev, "Invalid core telemetry buffer size, enforcing to default %u bytes\n", CORE_TELEMETRY_DEFAULT_BUFFER_SIZE); gxp_core_telemetry_buffer_size = CORE_TELEMETRY_DEFAULT_BUFFER_SIZE; } /* TODO(b/260959553): Remove mutex_lock/unlock during legacy telemetry removal */ mutex_lock(&mgr->lock); log_buff_data = allocate_telemetry_buffers(gxp, gxp_core_telemetry_buffer_size); if (IS_ERR_OR_NULL(log_buff_data)) { dev_warn(gxp->dev, "Failed to allocate per core log buffer of %u bytes\n", gxp_core_telemetry_buffer_size); ret = -ENOMEM; goto err_free_buffers; } trace_buff_data = allocate_telemetry_buffers(gxp, gxp_core_telemetry_buffer_size); if (IS_ERR_OR_NULL(trace_buff_data)) { dev_warn(gxp->dev, "Failed to allocate per core trace buffer of %u bytes\n", gxp_core_telemetry_buffer_size); free_telemetry_buffers(gxp, log_buff_data); ret = -ENOMEM; goto err_free_buffers; } ret = enable_telemetry_buffers(gxp, log_buff_data, GXP_TELEMETRY_TYPE_LOGGING); if (ret) { dev_warn(gxp->dev, "enable telemetry buffer failed (ret=%d)", ret); goto err_free; } ret = enable_telemetry_buffers(gxp, trace_buff_data, GXP_TELEMETRY_TYPE_TRACING); if (ret) { dev_warn(gxp->dev, "enable telemetry buffer failed (ret=%d)", ret); goto err_free; } gxp->core_telemetry_mgr->logging_buff_data = log_buff_data; gxp->core_telemetry_mgr->tracing_buff_data = trace_buff_data; mutex_unlock(&mgr->lock); return 0; err_free: free_telemetry_buffers(gxp, log_buff_data); free_telemetry_buffers(gxp, trace_buff_data); err_free_buffers: mutex_unlock(&mgr->lock); mutex_destroy(&mgr->lock); devm_kfree(gxp->dev, mgr); gxp->core_telemetry_mgr = NULL; return ret; } /* Wrapper struct to be used by the core telemetry vma_ops. */ struct telemetry_vma_data { struct gxp_dev *gxp; struct buffer_data *buff_data; u8 type; refcount_t ref_count; }; static void telemetry_vma_open(struct vm_area_struct *vma) { struct gxp_dev *gxp; struct telemetry_vma_data *vma_data = (struct telemetry_vma_data *)vma->vm_private_data; /* * vma_ops are required only for legacy telemetry flow * to keep track of buffer allocation during mmap and * buffer free during munmap. */ if (IS_ERR_OR_NULL(vma_data)) return; gxp = vma_data->gxp; mutex_lock(&gxp->core_telemetry_mgr->lock); refcount_inc(&vma_data->ref_count); mutex_unlock(&gxp->core_telemetry_mgr->lock); } static void telemetry_vma_close(struct vm_area_struct *vma) { struct gxp_dev *gxp; struct buffer_data *buff_data; u8 type; struct telemetry_vma_data *vma_data = (struct telemetry_vma_data *)vma->vm_private_data; /* * vma_ops are required only for legacy telemetry flow * to keep track of buffer allocation during mmap and * buffer free during munmap. */ if (IS_ERR_OR_NULL(vma_data)) return; gxp = vma_data->gxp; buff_data = vma_data->buff_data; type = vma_data->type; mutex_lock(&gxp->core_telemetry_mgr->lock); if (!refcount_dec_and_test(&vma_data->ref_count)) goto out; /* * Free the core telemetry buffers if they are no longer in use. * * If a client enabled core telemetry, then closed their VMA without * disabling it, firmware will still be expecting those buffers to be * mapped. If this is the case, core telemetry will be disabled, and the * buffers freed, when the client is closed. * * We cannot disable core telemetry here, since attempting to lock the * `vd_semaphore` while holding the mmap lock can lead to deadlocks. */ if (refcount_dec_and_test(&buff_data->ref_count)) { switch (type) { case GXP_TELEMETRY_TYPE_LOGGING: gxp->core_telemetry_mgr->logging_buff_data_legacy = NULL; break; case GXP_TELEMETRY_TYPE_TRACING: gxp->core_telemetry_mgr->tracing_buff_data_legacy = NULL; break; default: dev_warn(gxp->dev, "%s called with invalid type %u\n", __func__, type); } free_telemetry_buffers(gxp, buff_data); } kfree(vma_data); out: mutex_unlock(&gxp->core_telemetry_mgr->lock); } /* TODO(b/260959553): Remove vma ops during legacy telemetry removal */ static const struct vm_operations_struct telemetry_vma_ops = { .open = telemetry_vma_open, .close = telemetry_vma_close, }; /** * check_telemetry_type_availability() - Checks if @type is valid and whether * buffers of that type already exists. * @gxp: The GXP device to check availability for * @type: Either `GXP_TELEMETRY_TYPE_LOGGING` or `GXP_TELEMETRY_TYPE_TRACING` * * Caller must hold the core telemetry_manager's lock. * * Return: * * 0 - @type is valid and can have new buffers created * * -EBUSY - Buffers already exist for @type * * -EINVAL - @type is not a valid core telemetry type */ static int check_telemetry_type_availability(struct gxp_dev *gxp, u8 type) { lockdep_assert_held(&gxp->core_telemetry_mgr->lock); switch (type) { case GXP_TELEMETRY_TYPE_LOGGING: if (gxp->core_telemetry_mgr->logging_buff_data_legacy) return -EBUSY; break; case GXP_TELEMETRY_TYPE_TRACING: if (gxp->core_telemetry_mgr->tracing_buff_data_legacy) return -EBUSY; break; default: return -EINVAL; } return 0; } /** * allocate_telemetry_buffers() - Allocate and populate a `struct buffer_data`, * including allocating and mapping one coherent * buffer of @size bytes per core. * @gxp: The GXP device to allocate the buffers for * @size: The size of buffer to allocate for each core * * Caller must hold the core telemetry_manager's lock. * * Return: A pointer to the `struct buffer_data` if successful, error otherwise */ static struct buffer_data *allocate_telemetry_buffers(struct gxp_dev *gxp, size_t size) { struct buffer_data *data; int i; int ret = 0; size = size < PAGE_SIZE ? PAGE_SIZE : size; /* TODO(b/260959553): Remove lockdep_assert_held during legacy telemetry removal */ lockdep_assert_held(&gxp->core_telemetry_mgr->lock); data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return NULL; /* Allocate cache-coherent buffers for logging/tracing to */ for (i = 0; i < GXP_NUM_CORES; i++) { /* Allocate a coherent buffer in the default domain */ ret = gxp_dma_alloc_coherent_buf(gxp, NULL, size, GFP_KERNEL, 0, &data->buffers[i]); if (ret) { dev_err(gxp->dev, "Failed to allocate coherent buffer\n"); goto err_alloc; } } data->size = size; refcount_set(&data->ref_count, 1); data->is_enabled = false; return data; err_alloc: while (i--) gxp_dma_free_coherent_buf(gxp, NULL, &data->buffers[i]); kfree(data); return ERR_PTR(ret); } /** * free_telemetry_buffers() - Unmap and free a `struct buffer_data` * @gxp: The GXP device the buffers were allocated for * @data: The descriptor of the buffers to unmap and free * * Caller must hold the core telemetry_manager's lock. */ static void free_telemetry_buffers(struct gxp_dev *gxp, struct buffer_data *data) { int i; /* TODO(b/260959553): Remove lockdep_assert_held during legacy telemetry removal */ lockdep_assert_held(&gxp->core_telemetry_mgr->lock); for (i = 0; i < GXP_NUM_CORES; i++) gxp_dma_free_coherent_buf(gxp, NULL, &data->buffers[i]); kfree(data); } /** * remap_telemetry_buffers() - Remaps a set of core telemetry buffers into a * user-space vm_area. * @gxp: The GXP device the buffers were allocated for * @vma: A vm area to remap the buffers into * @buff_data: The data describing a set of core telemetry buffers to remap * * Caller must hold the core telemetry_manager's lock. * * Return: * * 0 - Success * * otherwise - Error returned by `remap_pfn_range()` */ static int remap_telemetry_buffers(struct gxp_dev *gxp, struct vm_area_struct *vma, struct buffer_data *buff_data) { unsigned long orig_pgoff = vma->vm_pgoff; int i; unsigned long offset; phys_addr_t phys; int ret = 0; lockdep_assert_held(&gxp->core_telemetry_mgr->lock); /* mmap the buffers */ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP; vma->vm_pgoff = 0; for (i = 0; i < GXP_NUM_CORES; i++) { /* * Remap each core's buffer a page at a time, in case it is not * physically contiguous. */ for (offset = 0; offset < buff_data->size; offset += PAGE_SIZE) { /* * `virt_to_phys()` does not work on memory allocated * by `dma_alloc_coherent()`, so we have to use * `iommu_iova_to_phys()` instead. Since all buffers * are mapped to the default domain as well as any per- * core domains, we can use it here to get the physical * address of any valid IOVA, regardless of its core. */ phys = iommu_iova_to_phys( iommu_get_domain_for_dev(gxp->dev), buff_data->buffers[i].dma_addr + offset); ret = remap_pfn_range( vma, vma->vm_start + buff_data->size * i + offset, phys >> PAGE_SHIFT, PAGE_SIZE, vma->vm_page_prot); if (ret) goto out; } } out: vma->vm_pgoff = orig_pgoff; /* TODO(b/260959553): Remove vma ops during legacy telemetry removal */ vma->vm_ops = &telemetry_vma_ops; return ret; } int gxp_core_telemetry_mmap_buffers(struct gxp_dev *gxp, u8 type, struct vm_area_struct *vma) { int ret = 0; struct buffer_data *buff_data; size_t total_size = vma->vm_end - vma->vm_start; size_t size = total_size / GXP_NUM_CORES; if (!gxp->core_telemetry_mgr) return -ENODEV; if (type == GXP_TELEMETRY_TYPE_LOGGING) buff_data = gxp->core_telemetry_mgr->logging_buff_data; else if (type == GXP_TELEMETRY_TYPE_TRACING) buff_data = gxp->core_telemetry_mgr->tracing_buff_data; else return -EINVAL; /* * Total size must divide evenly into a GXP_CORE_TELEMETRY_BUFFER_UNIT_SIZE * aligned buffer per core. */ if (!total_size || total_size % (GXP_CORE_TELEMETRY_BUFFER_UNIT_SIZE * GXP_NUM_CORES)) { dev_warn( gxp->dev, "Invalid vma size(%lu bytes) requested for telemetry\n", total_size); return -EINVAL; } /* * Per core log buffer size should be equal to pre allocated * aligned buffer per core. */ if (size != buff_data->size) { dev_warn( gxp->dev, "Invalid per core requested telemetry buffer size(%lu bytes)\n", size); return -EINVAL; } mutex_lock(&gxp->core_telemetry_mgr->lock); ret = remap_telemetry_buffers(gxp, vma, buff_data); if (ret) goto err; vma->vm_private_data = NULL; mutex_unlock(&gxp->core_telemetry_mgr->lock); return 0; err: mutex_unlock(&gxp->core_telemetry_mgr->lock); return ret; } int gxp_core_telemetry_mmap_buffers_legacy(struct gxp_dev *gxp, u8 type, struct vm_area_struct *vma) { int ret = 0; struct telemetry_vma_data *vma_data; size_t total_size = vma->vm_end - vma->vm_start; size_t size = total_size / GXP_NUM_CORES; struct buffer_data *buff_data; int i; if (!gxp->core_telemetry_mgr) return -ENODEV; /* Total size must divide evenly into 1 page-aligned buffer per core */ if (!total_size || total_size % (PAGE_SIZE * GXP_NUM_CORES)) return -EINVAL; mutex_lock(&gxp->core_telemetry_mgr->lock); ret = check_telemetry_type_availability(gxp, type); if (ret) goto err; vma_data = kmalloc(sizeof(*vma_data), GFP_KERNEL); if (!vma_data) { ret = -ENOMEM; goto err; } buff_data = allocate_telemetry_buffers(gxp, size); if (IS_ERR(buff_data)) { ret = PTR_ERR(buff_data); goto err_free_vma_data; } ret = remap_telemetry_buffers(gxp, vma, buff_data); if (ret) goto err_free_buffers; vma_data->gxp = gxp; vma_data->buff_data = buff_data; vma_data->type = type; refcount_set(&vma_data->ref_count, 1); vma->vm_private_data = vma_data; /* Save book-keeping on the buffers in the core telemetry manager */ if (type == GXP_TELEMETRY_TYPE_LOGGING) gxp->core_telemetry_mgr->logging_buff_data_legacy = buff_data; else /* type == GXP_TELEMETRY_TYPE_TRACING */ gxp->core_telemetry_mgr->tracing_buff_data_legacy = buff_data; mutex_unlock(&gxp->core_telemetry_mgr->lock); return 0; err_free_buffers: for (i = 0; i < GXP_NUM_CORES; i++) gxp_dma_free_coherent_buf(gxp, NULL, &buff_data->buffers[i]); kfree(buff_data); err_free_vma_data: kfree(vma_data); err: mutex_unlock(&gxp->core_telemetry_mgr->lock); return ret; } int gxp_core_telemetry_enable(struct gxp_dev *gxp, u8 type) { struct buffer_data *data; int ret = 0; uint core; struct gxp_virtual_device *vd; /* * `vd_semaphore` cannot be acquired while holding the core telemetry * lock, so acquire it here before locking the core telemetry lock. */ down_read(&gxp->vd_semaphore); mutex_lock(&gxp->core_telemetry_mgr->lock); switch (type) { case GXP_TELEMETRY_TYPE_LOGGING: data = gxp->core_telemetry_mgr->logging_buff_data_legacy; break; case GXP_TELEMETRY_TYPE_TRACING: data = gxp->core_telemetry_mgr->tracing_buff_data_legacy; break; default: ret = -EINVAL; goto out; } if (!data) { ret = -ENXIO; goto out; } /* Map the buffers for any cores already running */ for (core = 0; core < GXP_NUM_CORES; core++) { vd = gxp->core_to_vd[core]; if (vd != NULL) { ret = gxp_dma_map_allocated_coherent_buffer( gxp, &data->buffers[core], vd->domain, 0); if (ret) goto err; } } /* Populate the buffer fields in firmware-data */ data->host_status |= GXP_CORE_TELEMETRY_HOST_STATUS_ENABLED; gxp_fw_data_set_core_telemetry_descriptors(gxp, type, data->host_status, data->buffers, data->size); /* Notify any running cores that firmware-data was updated */ for (core = 0; core < GXP_NUM_CORES; core++) { if (gxp_is_fw_running(gxp, core)) gxp_notification_send(gxp, core, CORE_NOTIF_TELEMETRY_STATUS); } refcount_inc(&data->ref_count); data->is_enabled = true; goto out; err: while (core--) { vd = gxp->core_to_vd[core]; if (vd) gxp_dma_unmap_allocated_coherent_buffer( gxp, vd->domain, &data->buffers[core]); } out: mutex_unlock(&gxp->core_telemetry_mgr->lock); up_read(&gxp->vd_semaphore); return ret; } /** * 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 core 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 `core_telemetry_mgr->lock`. * Caller must hold `gxp->vd_semaphore` for reading only. * It is not allowed to hold `gxp->vd_semaphore` for writing, since this * function needs to release `gxp->vd_semaphore` at different points to sleep. * * 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; lockdep_assert_held(&gxp->core_telemetry_mgr->lock); lockdep_assert_held_read(&gxp->vd_semaphore); gxp_notification_send(gxp, core, CORE_NOTIF_TELEMETRY_STATUS); /* Wait for ACK from firmware */ while (is_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->core_telemetry_mgr->waitq, !is_telemetry_enabled(gxp, core, type), msecs_to_jiffies(10)); retries_left--; /* * No function may attempt to acquire the `vd_semaphore` while * holding the core telemetry lock, so it must be released, then * re-acquired once the `vd_semaphore` is held. */ mutex_unlock(&gxp->core_telemetry_mgr->lock); down_read(&gxp->vd_semaphore); mutex_lock(&gxp->core_telemetry_mgr->lock); } /* * 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_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 core telemetry so that it * can be invoked by internal functions that are * already holding the core 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 `core_telemetry_mgr->lock`. * Caller must hold `gxp->vd_semaphore` for reading only. * It is not allowed to hold `gxp->vd_semaphore` for writing, since this * function needs to release `gxp->vd_semaphore` at different points to sleep. * * 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; uint core; struct gxp_virtual_device *vd; lockdep_assert_held(&gxp->core_telemetry_mgr->lock); lockdep_assert_held_read(&gxp->vd_semaphore); /* Cleanup core telemetry manager's book-keeping */ switch (type) { case GXP_TELEMETRY_TYPE_LOGGING: data = gxp->core_telemetry_mgr->logging_buff_data_legacy; break; case GXP_TELEMETRY_TYPE_TRACING: data = gxp->core_telemetry_mgr->tracing_buff_data_legacy; break; default: return -EINVAL; } if (!data) return -ENXIO; if (!(data->host_status & GXP_CORE_TELEMETRY_HOST_STATUS_ENABLED)) return 0; data->is_enabled = false; /* Clear the log buffer fields in firmware-data */ data->host_status &= ~GXP_CORE_TELEMETRY_HOST_STATUS_ENABLED; gxp_fw_data_set_core_telemetry_descriptors(gxp, type, data->host_status, NULL, 0); /* Notify any running cores that firmware-data was updated */ 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); } vd = gxp->core_to_vd[core]; if (vd) gxp_dma_unmap_allocated_coherent_buffer( gxp, vd->domain, &data->buffers[core]); } if (refcount_dec_and_test(&data->ref_count)) { switch (type) { case GXP_TELEMETRY_TYPE_LOGGING: gxp->core_telemetry_mgr->logging_buff_data_legacy = NULL; break; case GXP_TELEMETRY_TYPE_TRACING: gxp->core_telemetry_mgr->tracing_buff_data_legacy = NULL; break; default: /* NO-OP, we returned above if `type` was invalid */ break; } free_telemetry_buffers(gxp, data); } return 0; } int gxp_core_telemetry_disable(struct gxp_dev *gxp, u8 type) { int ret; /* * `vd_semaphore` cannot be acquired while holding the core telemetry * lock, so acquire it here before locking the core telemetry lock. */ down_read(&gxp->vd_semaphore); mutex_lock(&gxp->core_telemetry_mgr->lock); ret = telemetry_disable_locked(gxp, type); mutex_unlock(&gxp->core_telemetry_mgr->lock); up_read(&gxp->vd_semaphore); return ret; } int gxp_core_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); mutex_lock(&gxp->core_telemetry_mgr->lock); switch (type) { case GXP_TELEMETRY_TYPE_LOGGING: ctx_to_set = &gxp->core_telemetry_mgr->logging_efd; break; case GXP_TELEMETRY_TYPE_TRACING: ctx_to_set = &gxp->core_telemetry_mgr->tracing_efd; break; default: ret = -EINVAL; eventfd_ctx_put(new_ctx); goto out; } if (*ctx_to_set) { dev_warn( gxp->dev, "Replacing existing core telemetry eventfd (type=%u)\n", type); eventfd_ctx_put(*ctx_to_set); } *ctx_to_set = new_ctx; out: mutex_unlock(&gxp->core_telemetry_mgr->lock); return ret; } int gxp_core_telemetry_unregister_eventfd(struct gxp_dev *gxp, u8 type) { int ret = 0; mutex_lock(&gxp->core_telemetry_mgr->lock); switch (type) { case GXP_TELEMETRY_TYPE_LOGGING: if (gxp->core_telemetry_mgr->logging_efd) eventfd_ctx_put(gxp->core_telemetry_mgr->logging_efd); gxp->core_telemetry_mgr->logging_efd = NULL; break; case GXP_TELEMETRY_TYPE_TRACING: if (gxp->core_telemetry_mgr->tracing_efd) eventfd_ctx_put(gxp->core_telemetry_mgr->tracing_efd); gxp->core_telemetry_mgr->tracing_efd = NULL; break; default: ret = -EINVAL; } mutex_unlock(&gxp->core_telemetry_mgr->lock); return ret; } struct work_struct * gxp_core_telemetry_get_notification_handler(struct gxp_dev *gxp, uint core) { struct gxp_core_telemetry_manager *mgr = gxp->core_telemetry_mgr; if (!mgr || core >= GXP_NUM_CORES) return NULL; return &mgr->notification_works[core].work; } void gxp_core_telemetry_exit(struct gxp_dev *gxp) { struct buffer_data *log_buff_data, *trace_buff_data; struct gxp_core_telemetry_manager *mgr = gxp->core_telemetry_mgr; if (!mgr) { dev_warn(gxp->dev, "Core telemetry manager was not allocated\n"); return; } /* TODO(b/260959553): Remove mutex_lock/unlock during legacy telemetry removal */ mutex_lock(&gxp->core_telemetry_mgr->lock); log_buff_data = mgr->logging_buff_data; trace_buff_data = mgr->tracing_buff_data; if (!IS_ERR_OR_NULL(log_buff_data)) free_telemetry_buffers(gxp, log_buff_data); if (!IS_ERR_OR_NULL(trace_buff_data)) free_telemetry_buffers(gxp, trace_buff_data); mutex_unlock(&gxp->core_telemetry_mgr->lock); if (!IS_ERR_OR_NULL(gxp->core_telemetry_mgr->logging_efd)) { dev_warn(gxp->dev, "logging_efd was not released\n"); eventfd_ctx_put(gxp->core_telemetry_mgr->logging_efd); gxp->core_telemetry_mgr->logging_efd = NULL; } if (!IS_ERR_OR_NULL(gxp->core_telemetry_mgr->tracing_efd)) { dev_warn(gxp->dev, "tracing_efd was not released\n"); eventfd_ctx_put(gxp->core_telemetry_mgr->tracing_efd); gxp->core_telemetry_mgr->tracing_efd = NULL; } mutex_destroy(&mgr->lock); devm_kfree(gxp->dev, mgr); gxp->core_telemetry_mgr = NULL; }