| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- // SPDX-License-Identifier: GPL-2.0-only OR MIT
- /*
- * Copyright (C) 2020-2024 Intel Corporation
- */
- #include <drm/drm_file.h>
- #include <linux/pm_runtime.h>
- #include "ivpu_drv.h"
- #include "ivpu_gem.h"
- #include "ivpu_hw.h"
- #include "ivpu_jsm_msg.h"
- #include "ivpu_ms.h"
- #include "ivpu_pm.h"
- #define MS_INFO_BUFFER_SIZE SZ_64K
- #define MS_NUM_BUFFERS 2
- #define MS_READ_PERIOD_MULTIPLIER 2
- #define MS_MIN_SAMPLE_PERIOD_NS 1000000
- static struct ivpu_ms_instance *
- get_instance_by_mask(struct ivpu_file_priv *file_priv, u64 metric_mask)
- {
- struct ivpu_ms_instance *ms;
- lockdep_assert_held(&file_priv->ms_lock);
- list_for_each_entry(ms, &file_priv->ms_instance_list, ms_instance_node)
- if (ms->mask == metric_mask)
- return ms;
- return NULL;
- }
- int ivpu_ms_start_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
- {
- struct ivpu_file_priv *file_priv = file->driver_priv;
- struct drm_ivpu_metric_streamer_start *args = data;
- struct ivpu_device *vdev = file_priv->vdev;
- struct ivpu_ms_instance *ms;
- u32 sample_size;
- u64 buf_size;
- int ret;
- if (!args->metric_group_mask || !args->read_period_samples ||
- args->sampling_period_ns < MS_MIN_SAMPLE_PERIOD_NS)
- return -EINVAL;
- ret = ivpu_rpm_get(vdev);
- if (ret < 0)
- return ret;
- mutex_lock(&file_priv->ms_lock);
- if (get_instance_by_mask(file_priv, args->metric_group_mask)) {
- ivpu_dbg(vdev, IOCTL, "Instance already exists (mask %#llx)\n",
- args->metric_group_mask);
- ret = -EALREADY;
- goto unlock;
- }
- ms = kzalloc_obj(*ms);
- if (!ms) {
- ret = -ENOMEM;
- goto unlock;
- }
- ms->mask = args->metric_group_mask;
- ret = ivpu_jsm_metric_streamer_info(vdev, ms->mask, 0, 0, &sample_size, NULL);
- if (ret)
- goto err_free_ms;
- buf_size = PAGE_ALIGN((u64)args->read_period_samples * sample_size *
- MS_READ_PERIOD_MULTIPLIER * MS_NUM_BUFFERS);
- if (buf_size > ivpu_hw_range_size(&vdev->hw->ranges.global)) {
- ivpu_dbg(vdev, IOCTL, "Requested MS buffer size %llu exceeds range size %llu\n",
- buf_size, ivpu_hw_range_size(&vdev->hw->ranges.global));
- ret = -EINVAL;
- goto err_free_ms;
- }
- ms->bo = ivpu_bo_create_global(vdev, buf_size, DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
- if (!ms->bo) {
- ivpu_dbg(vdev, IOCTL, "Failed to allocate MS buffer (size %llu)\n", buf_size);
- ret = -ENOMEM;
- goto err_free_ms;
- }
- ms->buff_size = ivpu_bo_size(ms->bo) / MS_NUM_BUFFERS;
- ms->active_buff_vpu_addr = ms->bo->vpu_addr;
- ms->inactive_buff_vpu_addr = ms->bo->vpu_addr + ms->buff_size;
- ms->active_buff_ptr = ivpu_bo_vaddr(ms->bo);
- ms->inactive_buff_ptr = ivpu_bo_vaddr(ms->bo) + ms->buff_size;
- ret = ivpu_jsm_metric_streamer_start(vdev, ms->mask, args->sampling_period_ns,
- ms->active_buff_vpu_addr, ms->buff_size);
- if (ret)
- goto err_free_bo;
- args->sample_size = sample_size;
- args->max_data_size = ivpu_bo_size(ms->bo);
- list_add_tail(&ms->ms_instance_node, &file_priv->ms_instance_list);
- goto unlock;
- err_free_bo:
- ivpu_bo_free(ms->bo);
- err_free_ms:
- kfree(ms);
- unlock:
- mutex_unlock(&file_priv->ms_lock);
- ivpu_rpm_put(vdev);
- return ret;
- }
- static int
- copy_leftover_bytes(struct ivpu_ms_instance *ms,
- void __user *user_ptr, u64 user_size, u64 *user_bytes_copied)
- {
- u64 copy_bytes;
- if (ms->leftover_bytes) {
- copy_bytes = min(user_size - *user_bytes_copied, ms->leftover_bytes);
- if (copy_to_user(user_ptr + *user_bytes_copied, ms->leftover_addr, copy_bytes))
- return -EFAULT;
- ms->leftover_bytes -= copy_bytes;
- ms->leftover_addr += copy_bytes;
- *user_bytes_copied += copy_bytes;
- }
- return 0;
- }
- static int
- copy_samples_to_user(struct ivpu_device *vdev, struct ivpu_ms_instance *ms,
- void __user *user_ptr, u64 user_size, u64 *user_bytes_copied)
- {
- u64 bytes_written;
- int ret;
- *user_bytes_copied = 0;
- ret = copy_leftover_bytes(ms, user_ptr, user_size, user_bytes_copied);
- if (ret)
- return ret;
- if (*user_bytes_copied == user_size)
- return 0;
- ret = ivpu_jsm_metric_streamer_update(vdev, ms->mask, ms->inactive_buff_vpu_addr,
- ms->buff_size, &bytes_written);
- if (ret)
- return ret;
- swap(ms->active_buff_vpu_addr, ms->inactive_buff_vpu_addr);
- swap(ms->active_buff_ptr, ms->inactive_buff_ptr);
- ms->leftover_bytes = bytes_written;
- ms->leftover_addr = ms->inactive_buff_ptr;
- return copy_leftover_bytes(ms, user_ptr, user_size, user_bytes_copied);
- }
- int ivpu_ms_get_data_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
- {
- struct drm_ivpu_metric_streamer_get_data *args = data;
- struct ivpu_file_priv *file_priv = file->driver_priv;
- struct ivpu_device *vdev = file_priv->vdev;
- struct ivpu_ms_instance *ms;
- u64 bytes_written;
- int ret;
- if (!args->metric_group_mask)
- return -EINVAL;
- ret = ivpu_rpm_get(vdev);
- if (ret < 0)
- return ret;
- mutex_lock(&file_priv->ms_lock);
- ms = get_instance_by_mask(file_priv, args->metric_group_mask);
- if (!ms) {
- ivpu_dbg(vdev, IOCTL, "Instance doesn't exist for mask: %#llx\n",
- args->metric_group_mask);
- ret = -EINVAL;
- goto unlock;
- }
- if (!args->buffer_size) {
- ret = ivpu_jsm_metric_streamer_update(vdev, ms->mask, 0, 0, &bytes_written);
- if (ret)
- goto unlock;
- args->data_size = bytes_written + ms->leftover_bytes;
- goto unlock;
- }
- if (!args->buffer_ptr) {
- ret = -EINVAL;
- goto unlock;
- }
- ret = copy_samples_to_user(vdev, ms, u64_to_user_ptr(args->buffer_ptr),
- args->buffer_size, &args->data_size);
- unlock:
- mutex_unlock(&file_priv->ms_lock);
- ivpu_rpm_put(vdev);
- return ret;
- }
- static void free_instance(struct ivpu_file_priv *file_priv, struct ivpu_ms_instance *ms)
- {
- lockdep_assert_held(&file_priv->ms_lock);
- list_del(&ms->ms_instance_node);
- ivpu_jsm_metric_streamer_stop(file_priv->vdev, ms->mask);
- ivpu_bo_free(ms->bo);
- kfree(ms);
- }
- int ivpu_ms_stop_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
- {
- struct ivpu_file_priv *file_priv = file->driver_priv;
- struct drm_ivpu_metric_streamer_stop *args = data;
- struct ivpu_device *vdev = file_priv->vdev;
- struct ivpu_ms_instance *ms;
- int ret;
- if (!args->metric_group_mask)
- return -EINVAL;
- ret = ivpu_rpm_get(vdev);
- if (ret < 0)
- return ret;
- mutex_lock(&file_priv->ms_lock);
- ms = get_instance_by_mask(file_priv, args->metric_group_mask);
- if (ms)
- free_instance(file_priv, ms);
- mutex_unlock(&file_priv->ms_lock);
- ivpu_rpm_put(vdev);
- return ms ? 0 : -EINVAL;
- }
- static inline struct ivpu_bo *get_ms_info_bo(struct ivpu_file_priv *file_priv)
- {
- lockdep_assert_held(&file_priv->ms_lock);
- if (file_priv->ms_info_bo)
- return file_priv->ms_info_bo;
- file_priv->ms_info_bo = ivpu_bo_create_global(file_priv->vdev, MS_INFO_BUFFER_SIZE,
- DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
- return file_priv->ms_info_bo;
- }
- int ivpu_ms_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
- {
- struct drm_ivpu_metric_streamer_get_data *args = data;
- struct ivpu_file_priv *file_priv = file->driver_priv;
- struct ivpu_device *vdev = file_priv->vdev;
- struct ivpu_bo *bo;
- u64 info_size;
- int ret;
- if (!args->metric_group_mask)
- return -EINVAL;
- if (!args->buffer_size)
- return ivpu_jsm_metric_streamer_info(vdev, args->metric_group_mask,
- 0, 0, NULL, &args->data_size);
- if (!args->buffer_ptr)
- return -EINVAL;
- mutex_lock(&file_priv->ms_lock);
- bo = get_ms_info_bo(file_priv);
- if (!bo) {
- ret = -ENOMEM;
- goto unlock;
- }
- ret = ivpu_jsm_metric_streamer_info(vdev, args->metric_group_mask, bo->vpu_addr,
- ivpu_bo_size(bo), NULL, &info_size);
- if (ret)
- goto unlock;
- if (args->buffer_size < info_size) {
- ret = -ENOSPC;
- goto unlock;
- }
- if (copy_to_user(u64_to_user_ptr(args->buffer_ptr), ivpu_bo_vaddr(bo), info_size))
- ret = -EFAULT;
- args->data_size = info_size;
- unlock:
- mutex_unlock(&file_priv->ms_lock);
- return ret;
- }
- void ivpu_ms_cleanup(struct ivpu_file_priv *file_priv)
- {
- struct ivpu_ms_instance *ms, *tmp;
- struct ivpu_device *vdev = file_priv->vdev;
- pm_runtime_get_sync(vdev->drm.dev);
- mutex_lock(&file_priv->ms_lock);
- if (file_priv->ms_info_bo) {
- ivpu_bo_free(file_priv->ms_info_bo);
- file_priv->ms_info_bo = NULL;
- }
- list_for_each_entry_safe(ms, tmp, &file_priv->ms_instance_list, ms_instance_node)
- free_instance(file_priv, ms);
- mutex_unlock(&file_priv->ms_lock);
- pm_runtime_put_autosuspend(vdev->drm.dev);
- }
- void ivpu_ms_cleanup_all(struct ivpu_device *vdev)
- {
- struct ivpu_file_priv *file_priv;
- unsigned long ctx_id;
- mutex_lock(&vdev->context_list_lock);
- xa_for_each(&vdev->context_xa, ctx_id, file_priv)
- ivpu_ms_cleanup(file_priv);
- mutex_unlock(&vdev->context_list_lock);
- }
|