xref: /linux/drivers/accel/ivpu/ivpu_ms.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0-only OR MIT
2 /*
3  * Copyright (C) 2020-2024 Intel Corporation
4  */
5 
6 #include <drm/drm_file.h>
7 
8 #include "ivpu_drv.h"
9 #include "ivpu_gem.h"
10 #include "ivpu_jsm_msg.h"
11 #include "ivpu_ms.h"
12 #include "ivpu_pm.h"
13 
14 #define MS_INFO_BUFFER_SIZE	  SZ_64K
15 #define MS_NUM_BUFFERS		  2
16 #define MS_READ_PERIOD_MULTIPLIER 2
17 #define MS_MIN_SAMPLE_PERIOD_NS   1000000
18 
19 static struct ivpu_ms_instance *
20 get_instance_by_mask(struct ivpu_file_priv *file_priv, u64 metric_mask)
21 {
22 	struct ivpu_ms_instance *ms;
23 
24 	lockdep_assert_held(&file_priv->ms_lock);
25 
26 	list_for_each_entry(ms, &file_priv->ms_instance_list, ms_instance_node)
27 		if (ms->mask == metric_mask)
28 			return ms;
29 
30 	return NULL;
31 }
32 
33 int ivpu_ms_start_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
34 {
35 	struct ivpu_file_priv *file_priv = file->driver_priv;
36 	struct drm_ivpu_metric_streamer_start *args = data;
37 	struct ivpu_device *vdev = file_priv->vdev;
38 	struct ivpu_ms_instance *ms;
39 	u64 single_buff_size;
40 	u32 sample_size;
41 	int ret;
42 
43 	if (!args->metric_group_mask || !args->read_period_samples ||
44 	    args->sampling_period_ns < MS_MIN_SAMPLE_PERIOD_NS)
45 		return -EINVAL;
46 
47 	mutex_lock(&file_priv->ms_lock);
48 
49 	if (get_instance_by_mask(file_priv, args->metric_group_mask)) {
50 		ivpu_err(vdev, "Instance already exists (mask %#llx)\n", args->metric_group_mask);
51 		ret = -EALREADY;
52 		goto unlock;
53 	}
54 
55 	ms = kzalloc(sizeof(*ms), GFP_KERNEL);
56 	if (!ms) {
57 		ret = -ENOMEM;
58 		goto unlock;
59 	}
60 
61 	ms->mask = args->metric_group_mask;
62 
63 	ret = ivpu_jsm_metric_streamer_info(vdev, ms->mask, 0, 0, &sample_size, NULL);
64 	if (ret)
65 		goto err_free_ms;
66 
67 	single_buff_size = sample_size *
68 		((u64)args->read_period_samples * MS_READ_PERIOD_MULTIPLIER);
69 	ms->bo = ivpu_bo_create_global(vdev, PAGE_ALIGN(single_buff_size * MS_NUM_BUFFERS),
70 				       DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
71 	if (!ms->bo) {
72 		ivpu_err(vdev, "Failed to allocate MS buffer (size %llu)\n", single_buff_size);
73 		ret = -ENOMEM;
74 		goto err_free_ms;
75 	}
76 
77 	ms->buff_size = ivpu_bo_size(ms->bo) / MS_NUM_BUFFERS;
78 	ms->active_buff_vpu_addr = ms->bo->vpu_addr;
79 	ms->inactive_buff_vpu_addr = ms->bo->vpu_addr + ms->buff_size;
80 	ms->active_buff_ptr = ivpu_bo_vaddr(ms->bo);
81 	ms->inactive_buff_ptr = ivpu_bo_vaddr(ms->bo) + ms->buff_size;
82 
83 	ret = ivpu_jsm_metric_streamer_start(vdev, ms->mask, args->sampling_period_ns,
84 					     ms->active_buff_vpu_addr, ms->buff_size);
85 	if (ret)
86 		goto err_free_bo;
87 
88 	args->sample_size = sample_size;
89 	args->max_data_size = ivpu_bo_size(ms->bo);
90 	list_add_tail(&ms->ms_instance_node, &file_priv->ms_instance_list);
91 	goto unlock;
92 
93 err_free_bo:
94 	ivpu_bo_free(ms->bo);
95 err_free_ms:
96 	kfree(ms);
97 unlock:
98 	mutex_unlock(&file_priv->ms_lock);
99 	return ret;
100 }
101 
102 static int
103 copy_leftover_bytes(struct ivpu_ms_instance *ms,
104 		    void __user *user_ptr, u64 user_size, u64 *user_bytes_copied)
105 {
106 	u64 copy_bytes;
107 
108 	if (ms->leftover_bytes) {
109 		copy_bytes = min(user_size - *user_bytes_copied, ms->leftover_bytes);
110 		if (copy_to_user(user_ptr + *user_bytes_copied, ms->leftover_addr, copy_bytes))
111 			return -EFAULT;
112 
113 		ms->leftover_bytes -= copy_bytes;
114 		ms->leftover_addr += copy_bytes;
115 		*user_bytes_copied += copy_bytes;
116 	}
117 
118 	return 0;
119 }
120 
121 static int
122 copy_samples_to_user(struct ivpu_device *vdev, struct ivpu_ms_instance *ms,
123 		     void __user *user_ptr, u64 user_size, u64 *user_bytes_copied)
124 {
125 	u64 bytes_written;
126 	int ret;
127 
128 	*user_bytes_copied = 0;
129 
130 	ret = copy_leftover_bytes(ms, user_ptr, user_size, user_bytes_copied);
131 	if (ret)
132 		return ret;
133 
134 	if (*user_bytes_copied == user_size)
135 		return 0;
136 
137 	ret = ivpu_jsm_metric_streamer_update(vdev, ms->mask, ms->inactive_buff_vpu_addr,
138 					      ms->buff_size, &bytes_written);
139 	if (ret)
140 		return ret;
141 
142 	swap(ms->active_buff_vpu_addr, ms->inactive_buff_vpu_addr);
143 	swap(ms->active_buff_ptr, ms->inactive_buff_ptr);
144 
145 	ms->leftover_bytes = bytes_written;
146 	ms->leftover_addr = ms->inactive_buff_ptr;
147 
148 	return copy_leftover_bytes(ms, user_ptr, user_size, user_bytes_copied);
149 }
150 
151 int ivpu_ms_get_data_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
152 {
153 	struct drm_ivpu_metric_streamer_get_data *args = data;
154 	struct ivpu_file_priv *file_priv = file->driver_priv;
155 	struct ivpu_device *vdev = file_priv->vdev;
156 	struct ivpu_ms_instance *ms;
157 	u64 bytes_written;
158 	int ret;
159 
160 	if (!args->metric_group_mask)
161 		return -EINVAL;
162 
163 	mutex_lock(&file_priv->ms_lock);
164 
165 	ms = get_instance_by_mask(file_priv, args->metric_group_mask);
166 	if (!ms) {
167 		ivpu_err(vdev, "Instance doesn't exist for mask: %#llx\n", args->metric_group_mask);
168 		ret = -EINVAL;
169 		goto unlock;
170 	}
171 
172 	if (!args->buffer_size) {
173 		ret = ivpu_jsm_metric_streamer_update(vdev, ms->mask, 0, 0, &bytes_written);
174 		if (ret)
175 			goto unlock;
176 		args->data_size = bytes_written + ms->leftover_bytes;
177 		goto unlock;
178 	}
179 
180 	if (!args->buffer_ptr) {
181 		ret = -EINVAL;
182 		goto unlock;
183 	}
184 
185 	ret = copy_samples_to_user(vdev, ms, u64_to_user_ptr(args->buffer_ptr),
186 				   args->buffer_size, &args->data_size);
187 unlock:
188 	mutex_unlock(&file_priv->ms_lock);
189 
190 	return ret;
191 }
192 
193 static void free_instance(struct ivpu_file_priv *file_priv, struct ivpu_ms_instance *ms)
194 {
195 	lockdep_assert_held(&file_priv->ms_lock);
196 
197 	list_del(&ms->ms_instance_node);
198 	ivpu_jsm_metric_streamer_stop(file_priv->vdev, ms->mask);
199 	ivpu_bo_free(ms->bo);
200 	kfree(ms);
201 }
202 
203 int ivpu_ms_stop_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
204 {
205 	struct ivpu_file_priv *file_priv = file->driver_priv;
206 	struct drm_ivpu_metric_streamer_stop *args = data;
207 	struct ivpu_ms_instance *ms;
208 
209 	if (!args->metric_group_mask)
210 		return -EINVAL;
211 
212 	mutex_lock(&file_priv->ms_lock);
213 
214 	ms = get_instance_by_mask(file_priv, args->metric_group_mask);
215 	if (ms)
216 		free_instance(file_priv, ms);
217 
218 	mutex_unlock(&file_priv->ms_lock);
219 
220 	return ms ? 0 : -EINVAL;
221 }
222 
223 static inline struct ivpu_bo *get_ms_info_bo(struct ivpu_file_priv *file_priv)
224 {
225 	lockdep_assert_held(&file_priv->ms_lock);
226 
227 	if (file_priv->ms_info_bo)
228 		return file_priv->ms_info_bo;
229 
230 	file_priv->ms_info_bo = ivpu_bo_create_global(file_priv->vdev, MS_INFO_BUFFER_SIZE,
231 						      DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
232 	return file_priv->ms_info_bo;
233 }
234 
235 int ivpu_ms_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
236 {
237 	struct drm_ivpu_metric_streamer_get_data *args = data;
238 	struct ivpu_file_priv *file_priv = file->driver_priv;
239 	struct ivpu_device *vdev = file_priv->vdev;
240 	struct ivpu_bo *bo;
241 	u64 info_size;
242 	int ret;
243 
244 	if (!args->metric_group_mask)
245 		return -EINVAL;
246 
247 	if (!args->buffer_size)
248 		return ivpu_jsm_metric_streamer_info(vdev, args->metric_group_mask,
249 						     0, 0, NULL, &args->data_size);
250 	if (!args->buffer_ptr)
251 		return -EINVAL;
252 
253 	mutex_lock(&file_priv->ms_lock);
254 
255 	bo = get_ms_info_bo(file_priv);
256 	if (!bo) {
257 		ret = -ENOMEM;
258 		goto unlock;
259 	}
260 
261 	ret = ivpu_jsm_metric_streamer_info(vdev, args->metric_group_mask, bo->vpu_addr,
262 					    ivpu_bo_size(bo), NULL, &info_size);
263 	if (ret)
264 		goto unlock;
265 
266 	if (args->buffer_size < info_size) {
267 		ret = -ENOSPC;
268 		goto unlock;
269 	}
270 
271 	if (copy_to_user(u64_to_user_ptr(args->buffer_ptr), ivpu_bo_vaddr(bo), info_size))
272 		ret = -EFAULT;
273 
274 	args->data_size = info_size;
275 unlock:
276 	mutex_unlock(&file_priv->ms_lock);
277 
278 	return ret;
279 }
280 
281 void ivpu_ms_cleanup(struct ivpu_file_priv *file_priv)
282 {
283 	struct ivpu_ms_instance *ms, *tmp;
284 
285 	mutex_lock(&file_priv->ms_lock);
286 
287 	if (file_priv->ms_info_bo) {
288 		ivpu_bo_free(file_priv->ms_info_bo);
289 		file_priv->ms_info_bo = NULL;
290 	}
291 
292 	list_for_each_entry_safe(ms, tmp, &file_priv->ms_instance_list, ms_instance_node)
293 		free_instance(file_priv, ms);
294 
295 	mutex_unlock(&file_priv->ms_lock);
296 }
297 
298 void ivpu_ms_cleanup_all(struct ivpu_device *vdev)
299 {
300 	struct ivpu_file_priv *file_priv;
301 	unsigned long ctx_id;
302 
303 	mutex_lock(&vdev->context_list_lock);
304 
305 	xa_for_each(&vdev->context_xa, ctx_id, file_priv)
306 		ivpu_ms_cleanup(file_priv);
307 
308 	mutex_unlock(&vdev->context_list_lock);
309 }
310