xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/i915/i915_query.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * SPDX-License-Identifier: MIT
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright © 2018 Intel Corporation
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/nospec.h>
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include "i915_drv.h"
10*4882a593Smuzhiyun #include "i915_perf.h"
11*4882a593Smuzhiyun #include "i915_query.h"
12*4882a593Smuzhiyun #include <uapi/drm/i915_drm.h>
13*4882a593Smuzhiyun 
copy_query_item(void * query_hdr,size_t query_sz,u32 total_length,struct drm_i915_query_item * query_item)14*4882a593Smuzhiyun static int copy_query_item(void *query_hdr, size_t query_sz,
15*4882a593Smuzhiyun 			   u32 total_length,
16*4882a593Smuzhiyun 			   struct drm_i915_query_item *query_item)
17*4882a593Smuzhiyun {
18*4882a593Smuzhiyun 	if (query_item->length == 0)
19*4882a593Smuzhiyun 		return total_length;
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun 	if (query_item->length < total_length)
22*4882a593Smuzhiyun 		return -EINVAL;
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun 	if (copy_from_user(query_hdr, u64_to_user_ptr(query_item->data_ptr),
25*4882a593Smuzhiyun 			   query_sz))
26*4882a593Smuzhiyun 		return -EFAULT;
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun 	return 0;
29*4882a593Smuzhiyun }
30*4882a593Smuzhiyun 
query_topology_info(struct drm_i915_private * dev_priv,struct drm_i915_query_item * query_item)31*4882a593Smuzhiyun static int query_topology_info(struct drm_i915_private *dev_priv,
32*4882a593Smuzhiyun 			       struct drm_i915_query_item *query_item)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	const struct sseu_dev_info *sseu = &dev_priv->gt.info.sseu;
35*4882a593Smuzhiyun 	struct drm_i915_query_topology_info topo;
36*4882a593Smuzhiyun 	u32 slice_length, subslice_length, eu_length, total_length;
37*4882a593Smuzhiyun 	int ret;
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 	if (query_item->flags != 0)
40*4882a593Smuzhiyun 		return -EINVAL;
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 	if (sseu->max_slices == 0)
43*4882a593Smuzhiyun 		return -ENODEV;
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 	BUILD_BUG_ON(sizeof(u8) != sizeof(sseu->slice_mask));
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	slice_length = sizeof(sseu->slice_mask);
48*4882a593Smuzhiyun 	subslice_length = sseu->max_slices * sseu->ss_stride;
49*4882a593Smuzhiyun 	eu_length = sseu->max_slices * sseu->max_subslices * sseu->eu_stride;
50*4882a593Smuzhiyun 	total_length = sizeof(topo) + slice_length + subslice_length +
51*4882a593Smuzhiyun 		       eu_length;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	ret = copy_query_item(&topo, sizeof(topo), total_length,
54*4882a593Smuzhiyun 			      query_item);
55*4882a593Smuzhiyun 	if (ret != 0)
56*4882a593Smuzhiyun 		return ret;
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	if (topo.flags != 0)
59*4882a593Smuzhiyun 		return -EINVAL;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	memset(&topo, 0, sizeof(topo));
62*4882a593Smuzhiyun 	topo.max_slices = sseu->max_slices;
63*4882a593Smuzhiyun 	topo.max_subslices = sseu->max_subslices;
64*4882a593Smuzhiyun 	topo.max_eus_per_subslice = sseu->max_eus_per_subslice;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	topo.subslice_offset = slice_length;
67*4882a593Smuzhiyun 	topo.subslice_stride = sseu->ss_stride;
68*4882a593Smuzhiyun 	topo.eu_offset = slice_length + subslice_length;
69*4882a593Smuzhiyun 	topo.eu_stride = sseu->eu_stride;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	if (copy_to_user(u64_to_user_ptr(query_item->data_ptr),
72*4882a593Smuzhiyun 			   &topo, sizeof(topo)))
73*4882a593Smuzhiyun 		return -EFAULT;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	if (copy_to_user(u64_to_user_ptr(query_item->data_ptr + sizeof(topo)),
76*4882a593Smuzhiyun 			   &sseu->slice_mask, slice_length))
77*4882a593Smuzhiyun 		return -EFAULT;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	if (copy_to_user(u64_to_user_ptr(query_item->data_ptr +
80*4882a593Smuzhiyun 					   sizeof(topo) + slice_length),
81*4882a593Smuzhiyun 			   sseu->subslice_mask, subslice_length))
82*4882a593Smuzhiyun 		return -EFAULT;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	if (copy_to_user(u64_to_user_ptr(query_item->data_ptr +
85*4882a593Smuzhiyun 					   sizeof(topo) +
86*4882a593Smuzhiyun 					   slice_length + subslice_length),
87*4882a593Smuzhiyun 			   sseu->eu_mask, eu_length))
88*4882a593Smuzhiyun 		return -EFAULT;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	return total_length;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun static int
query_engine_info(struct drm_i915_private * i915,struct drm_i915_query_item * query_item)94*4882a593Smuzhiyun query_engine_info(struct drm_i915_private *i915,
95*4882a593Smuzhiyun 		  struct drm_i915_query_item *query_item)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	struct drm_i915_query_engine_info __user *query_ptr =
98*4882a593Smuzhiyun 				u64_to_user_ptr(query_item->data_ptr);
99*4882a593Smuzhiyun 	struct drm_i915_engine_info __user *info_ptr;
100*4882a593Smuzhiyun 	struct drm_i915_query_engine_info query;
101*4882a593Smuzhiyun 	struct drm_i915_engine_info info = { };
102*4882a593Smuzhiyun 	unsigned int num_uabi_engines = 0;
103*4882a593Smuzhiyun 	struct intel_engine_cs *engine;
104*4882a593Smuzhiyun 	int len, ret;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	if (query_item->flags)
107*4882a593Smuzhiyun 		return -EINVAL;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	for_each_uabi_engine(engine, i915)
110*4882a593Smuzhiyun 		num_uabi_engines++;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	len = struct_size(query_ptr, engines, num_uabi_engines);
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	ret = copy_query_item(&query, sizeof(query), len, query_item);
115*4882a593Smuzhiyun 	if (ret != 0)
116*4882a593Smuzhiyun 		return ret;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	if (query.num_engines || query.rsvd[0] || query.rsvd[1] ||
119*4882a593Smuzhiyun 	    query.rsvd[2])
120*4882a593Smuzhiyun 		return -EINVAL;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	info_ptr = &query_ptr->engines[0];
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	for_each_uabi_engine(engine, i915) {
125*4882a593Smuzhiyun 		info.engine.engine_class = engine->uabi_class;
126*4882a593Smuzhiyun 		info.engine.engine_instance = engine->uabi_instance;
127*4882a593Smuzhiyun 		info.capabilities = engine->uabi_capabilities;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 		if (copy_to_user(info_ptr, &info, sizeof(info)))
130*4882a593Smuzhiyun 			return -EFAULT;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 		query.num_engines++;
133*4882a593Smuzhiyun 		info_ptr++;
134*4882a593Smuzhiyun 	}
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	if (copy_to_user(query_ptr, &query, sizeof(query)))
137*4882a593Smuzhiyun 		return -EFAULT;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	return len;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun 
can_copy_perf_config_registers_or_number(u32 user_n_regs,u64 user_regs_ptr,u32 kernel_n_regs)142*4882a593Smuzhiyun static int can_copy_perf_config_registers_or_number(u32 user_n_regs,
143*4882a593Smuzhiyun 						    u64 user_regs_ptr,
144*4882a593Smuzhiyun 						    u32 kernel_n_regs)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	/*
147*4882a593Smuzhiyun 	 * We'll just put the number of registers, and won't copy the
148*4882a593Smuzhiyun 	 * register.
149*4882a593Smuzhiyun 	 */
150*4882a593Smuzhiyun 	if (user_n_regs == 0)
151*4882a593Smuzhiyun 		return 0;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	if (user_n_regs < kernel_n_regs)
154*4882a593Smuzhiyun 		return -EINVAL;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	return 0;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun 
copy_perf_config_registers_or_number(const struct i915_oa_reg * kernel_regs,u32 kernel_n_regs,u64 user_regs_ptr,u32 * user_n_regs)159*4882a593Smuzhiyun static int copy_perf_config_registers_or_number(const struct i915_oa_reg *kernel_regs,
160*4882a593Smuzhiyun 						u32 kernel_n_regs,
161*4882a593Smuzhiyun 						u64 user_regs_ptr,
162*4882a593Smuzhiyun 						u32 *user_n_regs)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun 	u32 __user *p = u64_to_user_ptr(user_regs_ptr);
165*4882a593Smuzhiyun 	u32 r;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	if (*user_n_regs == 0) {
168*4882a593Smuzhiyun 		*user_n_regs = kernel_n_regs;
169*4882a593Smuzhiyun 		return 0;
170*4882a593Smuzhiyun 	}
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	*user_n_regs = kernel_n_regs;
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	if (!user_write_access_begin(p, 2 * sizeof(u32) * kernel_n_regs))
175*4882a593Smuzhiyun 		return -EFAULT;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	for (r = 0; r < kernel_n_regs; r++, p += 2) {
178*4882a593Smuzhiyun 		unsafe_put_user(i915_mmio_reg_offset(kernel_regs[r].addr),
179*4882a593Smuzhiyun 				p, Efault);
180*4882a593Smuzhiyun 		unsafe_put_user(kernel_regs[r].value, p + 1, Efault);
181*4882a593Smuzhiyun 	}
182*4882a593Smuzhiyun 	user_write_access_end();
183*4882a593Smuzhiyun 	return 0;
184*4882a593Smuzhiyun Efault:
185*4882a593Smuzhiyun 	user_write_access_end();
186*4882a593Smuzhiyun 	return -EFAULT;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
query_perf_config_data(struct drm_i915_private * i915,struct drm_i915_query_item * query_item,bool use_uuid)189*4882a593Smuzhiyun static int query_perf_config_data(struct drm_i915_private *i915,
190*4882a593Smuzhiyun 				  struct drm_i915_query_item *query_item,
191*4882a593Smuzhiyun 				  bool use_uuid)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun 	struct drm_i915_query_perf_config __user *user_query_config_ptr =
194*4882a593Smuzhiyun 		u64_to_user_ptr(query_item->data_ptr);
195*4882a593Smuzhiyun 	struct drm_i915_perf_oa_config __user *user_config_ptr =
196*4882a593Smuzhiyun 		u64_to_user_ptr(query_item->data_ptr +
197*4882a593Smuzhiyun 				sizeof(struct drm_i915_query_perf_config));
198*4882a593Smuzhiyun 	struct drm_i915_perf_oa_config user_config;
199*4882a593Smuzhiyun 	struct i915_perf *perf = &i915->perf;
200*4882a593Smuzhiyun 	struct i915_oa_config *oa_config;
201*4882a593Smuzhiyun 	char uuid[UUID_STRING_LEN + 1];
202*4882a593Smuzhiyun 	u64 config_id;
203*4882a593Smuzhiyun 	u32 flags, total_size;
204*4882a593Smuzhiyun 	int ret;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	if (!perf->i915)
207*4882a593Smuzhiyun 		return -ENODEV;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	total_size =
210*4882a593Smuzhiyun 		sizeof(struct drm_i915_query_perf_config) +
211*4882a593Smuzhiyun 		sizeof(struct drm_i915_perf_oa_config);
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	if (query_item->length == 0)
214*4882a593Smuzhiyun 		return total_size;
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	if (query_item->length < total_size) {
217*4882a593Smuzhiyun 		DRM_DEBUG("Invalid query config data item size=%u expected=%u\n",
218*4882a593Smuzhiyun 			  query_item->length, total_size);
219*4882a593Smuzhiyun 		return -EINVAL;
220*4882a593Smuzhiyun 	}
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	if (get_user(flags, &user_query_config_ptr->flags))
223*4882a593Smuzhiyun 		return -EFAULT;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	if (flags != 0)
226*4882a593Smuzhiyun 		return -EINVAL;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	if (use_uuid) {
229*4882a593Smuzhiyun 		struct i915_oa_config *tmp;
230*4882a593Smuzhiyun 		int id;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 		BUILD_BUG_ON(sizeof(user_query_config_ptr->uuid) >= sizeof(uuid));
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 		memset(&uuid, 0, sizeof(uuid));
235*4882a593Smuzhiyun 		if (copy_from_user(uuid, user_query_config_ptr->uuid,
236*4882a593Smuzhiyun 				     sizeof(user_query_config_ptr->uuid)))
237*4882a593Smuzhiyun 			return -EFAULT;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 		oa_config = NULL;
240*4882a593Smuzhiyun 		rcu_read_lock();
241*4882a593Smuzhiyun 		idr_for_each_entry(&perf->metrics_idr, tmp, id) {
242*4882a593Smuzhiyun 			if (!strcmp(tmp->uuid, uuid)) {
243*4882a593Smuzhiyun 				oa_config = i915_oa_config_get(tmp);
244*4882a593Smuzhiyun 				break;
245*4882a593Smuzhiyun 			}
246*4882a593Smuzhiyun 		}
247*4882a593Smuzhiyun 		rcu_read_unlock();
248*4882a593Smuzhiyun 	} else {
249*4882a593Smuzhiyun 		if (get_user(config_id, &user_query_config_ptr->config))
250*4882a593Smuzhiyun 			return -EFAULT;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 		oa_config = i915_perf_get_oa_config(perf, config_id);
253*4882a593Smuzhiyun 	}
254*4882a593Smuzhiyun 	if (!oa_config)
255*4882a593Smuzhiyun 		return -ENOENT;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	if (copy_from_user(&user_config, user_config_ptr, sizeof(user_config))) {
258*4882a593Smuzhiyun 		ret = -EFAULT;
259*4882a593Smuzhiyun 		goto out;
260*4882a593Smuzhiyun 	}
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	ret = can_copy_perf_config_registers_or_number(user_config.n_boolean_regs,
263*4882a593Smuzhiyun 						       user_config.boolean_regs_ptr,
264*4882a593Smuzhiyun 						       oa_config->b_counter_regs_len);
265*4882a593Smuzhiyun 	if (ret)
266*4882a593Smuzhiyun 		goto out;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	ret = can_copy_perf_config_registers_or_number(user_config.n_flex_regs,
269*4882a593Smuzhiyun 						       user_config.flex_regs_ptr,
270*4882a593Smuzhiyun 						       oa_config->flex_regs_len);
271*4882a593Smuzhiyun 	if (ret)
272*4882a593Smuzhiyun 		goto out;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	ret = can_copy_perf_config_registers_or_number(user_config.n_mux_regs,
275*4882a593Smuzhiyun 						       user_config.mux_regs_ptr,
276*4882a593Smuzhiyun 						       oa_config->mux_regs_len);
277*4882a593Smuzhiyun 	if (ret)
278*4882a593Smuzhiyun 		goto out;
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	ret = copy_perf_config_registers_or_number(oa_config->b_counter_regs,
281*4882a593Smuzhiyun 						   oa_config->b_counter_regs_len,
282*4882a593Smuzhiyun 						   user_config.boolean_regs_ptr,
283*4882a593Smuzhiyun 						   &user_config.n_boolean_regs);
284*4882a593Smuzhiyun 	if (ret)
285*4882a593Smuzhiyun 		goto out;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	ret = copy_perf_config_registers_or_number(oa_config->flex_regs,
288*4882a593Smuzhiyun 						   oa_config->flex_regs_len,
289*4882a593Smuzhiyun 						   user_config.flex_regs_ptr,
290*4882a593Smuzhiyun 						   &user_config.n_flex_regs);
291*4882a593Smuzhiyun 	if (ret)
292*4882a593Smuzhiyun 		goto out;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	ret = copy_perf_config_registers_or_number(oa_config->mux_regs,
295*4882a593Smuzhiyun 						   oa_config->mux_regs_len,
296*4882a593Smuzhiyun 						   user_config.mux_regs_ptr,
297*4882a593Smuzhiyun 						   &user_config.n_mux_regs);
298*4882a593Smuzhiyun 	if (ret)
299*4882a593Smuzhiyun 		goto out;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	memcpy(user_config.uuid, oa_config->uuid, sizeof(user_config.uuid));
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	if (copy_to_user(user_config_ptr, &user_config, sizeof(user_config))) {
304*4882a593Smuzhiyun 		ret = -EFAULT;
305*4882a593Smuzhiyun 		goto out;
306*4882a593Smuzhiyun 	}
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	ret = total_size;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun out:
311*4882a593Smuzhiyun 	i915_oa_config_put(oa_config);
312*4882a593Smuzhiyun 	return ret;
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun 
sizeof_perf_config_list(size_t count)315*4882a593Smuzhiyun static size_t sizeof_perf_config_list(size_t count)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun 	return sizeof(struct drm_i915_query_perf_config) + sizeof(u64) * count;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun 
sizeof_perf_metrics(struct i915_perf * perf)320*4882a593Smuzhiyun static size_t sizeof_perf_metrics(struct i915_perf *perf)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun 	struct i915_oa_config *tmp;
323*4882a593Smuzhiyun 	size_t i;
324*4882a593Smuzhiyun 	int id;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	i = 1;
327*4882a593Smuzhiyun 	rcu_read_lock();
328*4882a593Smuzhiyun 	idr_for_each_entry(&perf->metrics_idr, tmp, id)
329*4882a593Smuzhiyun 		i++;
330*4882a593Smuzhiyun 	rcu_read_unlock();
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	return sizeof_perf_config_list(i);
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun 
query_perf_config_list(struct drm_i915_private * i915,struct drm_i915_query_item * query_item)335*4882a593Smuzhiyun static int query_perf_config_list(struct drm_i915_private *i915,
336*4882a593Smuzhiyun 				  struct drm_i915_query_item *query_item)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun 	struct drm_i915_query_perf_config __user *user_query_config_ptr =
339*4882a593Smuzhiyun 		u64_to_user_ptr(query_item->data_ptr);
340*4882a593Smuzhiyun 	struct i915_perf *perf = &i915->perf;
341*4882a593Smuzhiyun 	u64 *oa_config_ids = NULL;
342*4882a593Smuzhiyun 	int alloc, n_configs;
343*4882a593Smuzhiyun 	u32 flags;
344*4882a593Smuzhiyun 	int ret;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	if (!perf->i915)
347*4882a593Smuzhiyun 		return -ENODEV;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	if (query_item->length == 0)
350*4882a593Smuzhiyun 		return sizeof_perf_metrics(perf);
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	if (get_user(flags, &user_query_config_ptr->flags))
353*4882a593Smuzhiyun 		return -EFAULT;
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	if (flags != 0)
356*4882a593Smuzhiyun 		return -EINVAL;
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	n_configs = 1;
359*4882a593Smuzhiyun 	do {
360*4882a593Smuzhiyun 		struct i915_oa_config *tmp;
361*4882a593Smuzhiyun 		u64 *ids;
362*4882a593Smuzhiyun 		int id;
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 		ids = krealloc(oa_config_ids,
365*4882a593Smuzhiyun 			       n_configs * sizeof(*oa_config_ids),
366*4882a593Smuzhiyun 			       GFP_KERNEL);
367*4882a593Smuzhiyun 		if (!ids)
368*4882a593Smuzhiyun 			return -ENOMEM;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 		alloc = fetch_and_zero(&n_configs);
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 		ids[n_configs++] = 1ull; /* reserved for test_config */
373*4882a593Smuzhiyun 		rcu_read_lock();
374*4882a593Smuzhiyun 		idr_for_each_entry(&perf->metrics_idr, tmp, id) {
375*4882a593Smuzhiyun 			if (n_configs < alloc)
376*4882a593Smuzhiyun 				ids[n_configs] = id;
377*4882a593Smuzhiyun 			n_configs++;
378*4882a593Smuzhiyun 		}
379*4882a593Smuzhiyun 		rcu_read_unlock();
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 		oa_config_ids = ids;
382*4882a593Smuzhiyun 	} while (n_configs > alloc);
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	if (query_item->length < sizeof_perf_config_list(n_configs)) {
385*4882a593Smuzhiyun 		DRM_DEBUG("Invalid query config list item size=%u expected=%zu\n",
386*4882a593Smuzhiyun 			  query_item->length,
387*4882a593Smuzhiyun 			  sizeof_perf_config_list(n_configs));
388*4882a593Smuzhiyun 		kfree(oa_config_ids);
389*4882a593Smuzhiyun 		return -EINVAL;
390*4882a593Smuzhiyun 	}
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	if (put_user(n_configs, &user_query_config_ptr->config)) {
393*4882a593Smuzhiyun 		kfree(oa_config_ids);
394*4882a593Smuzhiyun 		return -EFAULT;
395*4882a593Smuzhiyun 	}
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	ret = copy_to_user(user_query_config_ptr + 1,
398*4882a593Smuzhiyun 			   oa_config_ids,
399*4882a593Smuzhiyun 			   n_configs * sizeof(*oa_config_ids));
400*4882a593Smuzhiyun 	kfree(oa_config_ids);
401*4882a593Smuzhiyun 	if (ret)
402*4882a593Smuzhiyun 		return -EFAULT;
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	return sizeof_perf_config_list(n_configs);
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun 
query_perf_config(struct drm_i915_private * i915,struct drm_i915_query_item * query_item)407*4882a593Smuzhiyun static int query_perf_config(struct drm_i915_private *i915,
408*4882a593Smuzhiyun 			     struct drm_i915_query_item *query_item)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun 	switch (query_item->flags) {
411*4882a593Smuzhiyun 	case DRM_I915_QUERY_PERF_CONFIG_LIST:
412*4882a593Smuzhiyun 		return query_perf_config_list(i915, query_item);
413*4882a593Smuzhiyun 	case DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_UUID:
414*4882a593Smuzhiyun 		return query_perf_config_data(i915, query_item, true);
415*4882a593Smuzhiyun 	case DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_ID:
416*4882a593Smuzhiyun 		return query_perf_config_data(i915, query_item, false);
417*4882a593Smuzhiyun 	default:
418*4882a593Smuzhiyun 		return -EINVAL;
419*4882a593Smuzhiyun 	}
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun static int (* const i915_query_funcs[])(struct drm_i915_private *dev_priv,
423*4882a593Smuzhiyun 					struct drm_i915_query_item *query_item) = {
424*4882a593Smuzhiyun 	query_topology_info,
425*4882a593Smuzhiyun 	query_engine_info,
426*4882a593Smuzhiyun 	query_perf_config,
427*4882a593Smuzhiyun };
428*4882a593Smuzhiyun 
i915_query_ioctl(struct drm_device * dev,void * data,struct drm_file * file)429*4882a593Smuzhiyun int i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
430*4882a593Smuzhiyun {
431*4882a593Smuzhiyun 	struct drm_i915_private *dev_priv = to_i915(dev);
432*4882a593Smuzhiyun 	struct drm_i915_query *args = data;
433*4882a593Smuzhiyun 	struct drm_i915_query_item __user *user_item_ptr =
434*4882a593Smuzhiyun 		u64_to_user_ptr(args->items_ptr);
435*4882a593Smuzhiyun 	u32 i;
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	if (args->flags != 0)
438*4882a593Smuzhiyun 		return -EINVAL;
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	for (i = 0; i < args->num_items; i++, user_item_ptr++) {
441*4882a593Smuzhiyun 		struct drm_i915_query_item item;
442*4882a593Smuzhiyun 		unsigned long func_idx;
443*4882a593Smuzhiyun 		int ret;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 		if (copy_from_user(&item, user_item_ptr, sizeof(item)))
446*4882a593Smuzhiyun 			return -EFAULT;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 		if (item.query_id == 0)
449*4882a593Smuzhiyun 			return -EINVAL;
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun 		if (overflows_type(item.query_id - 1, unsigned long))
452*4882a593Smuzhiyun 			return -EINVAL;
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 		func_idx = item.query_id - 1;
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 		ret = -EINVAL;
457*4882a593Smuzhiyun 		if (func_idx < ARRAY_SIZE(i915_query_funcs)) {
458*4882a593Smuzhiyun 			func_idx = array_index_nospec(func_idx,
459*4882a593Smuzhiyun 						      ARRAY_SIZE(i915_query_funcs));
460*4882a593Smuzhiyun 			ret = i915_query_funcs[func_idx](dev_priv, &item);
461*4882a593Smuzhiyun 		}
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 		/* Only write the length back to userspace if they differ. */
464*4882a593Smuzhiyun 		if (ret != item.length && put_user(ret, &user_item_ptr->length))
465*4882a593Smuzhiyun 			return -EFAULT;
466*4882a593Smuzhiyun 	}
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	return 0;
469*4882a593Smuzhiyun }
470