1 | /* |
2 | * Copyright(c) 2011-2017 Intel Corporation. All rights reserved. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice (including the next |
12 | * paragraph) shall be included in all copies or substantial portions of the |
13 | * Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
21 | * SOFTWARE. |
22 | */ |
23 | #include <linux/debugfs.h> |
24 | #include <linux/list_sort.h> |
25 | #include "i915_drv.h" |
26 | #include "gvt.h" |
27 | |
28 | struct mmio_diff_param { |
29 | struct intel_vgpu *vgpu; |
30 | int total; |
31 | int diff; |
32 | struct list_head diff_mmio_list; |
33 | }; |
34 | |
35 | struct diff_mmio { |
36 | struct list_head node; |
37 | u32 offset; |
38 | u32 preg; |
39 | u32 vreg; |
40 | }; |
41 | |
42 | /* Compare two diff_mmio items. */ |
43 | static int mmio_offset_compare(void *priv, |
44 | const struct list_head *a, const struct list_head *b) |
45 | { |
46 | struct diff_mmio *ma; |
47 | struct diff_mmio *mb; |
48 | |
49 | ma = container_of(a, struct diff_mmio, node); |
50 | mb = container_of(b, struct diff_mmio, node); |
51 | if (ma->offset < mb->offset) |
52 | return -1; |
53 | else if (ma->offset > mb->offset) |
54 | return 1; |
55 | return 0; |
56 | } |
57 | |
58 | static inline int mmio_diff_handler(struct intel_gvt *gvt, |
59 | u32 offset, void *data) |
60 | { |
61 | struct mmio_diff_param *param = data; |
62 | struct diff_mmio *node; |
63 | u32 preg, vreg; |
64 | |
65 | preg = intel_uncore_read_notrace(uncore: gvt->gt->uncore, _MMIO(offset)); |
66 | vreg = vgpu_vreg(param->vgpu, offset); |
67 | |
68 | if (preg != vreg) { |
69 | node = kmalloc(size: sizeof(*node), GFP_ATOMIC); |
70 | if (!node) |
71 | return -ENOMEM; |
72 | |
73 | node->offset = offset; |
74 | node->preg = preg; |
75 | node->vreg = vreg; |
76 | list_add(new: &node->node, head: ¶m->diff_mmio_list); |
77 | param->diff++; |
78 | } |
79 | param->total++; |
80 | return 0; |
81 | } |
82 | |
83 | /* Show the all the different values of tracked mmio. */ |
84 | static int vgpu_mmio_diff_show(struct seq_file *s, void *unused) |
85 | { |
86 | struct intel_vgpu *vgpu = s->private; |
87 | struct intel_gvt *gvt = vgpu->gvt; |
88 | struct mmio_diff_param param = { |
89 | .vgpu = vgpu, |
90 | .total = 0, |
91 | .diff = 0, |
92 | }; |
93 | struct diff_mmio *node, *next; |
94 | |
95 | INIT_LIST_HEAD(list: ¶m.diff_mmio_list); |
96 | |
97 | mutex_lock(&gvt->lock); |
98 | spin_lock_bh(lock: &gvt->scheduler.mmio_context_lock); |
99 | |
100 | mmio_hw_access_pre(gt: gvt->gt); |
101 | /* Recognize all the diff mmios to list. */ |
102 | intel_gvt_for_each_tracked_mmio(gvt, handler: mmio_diff_handler, data: ¶m); |
103 | mmio_hw_access_post(gt: gvt->gt); |
104 | |
105 | spin_unlock_bh(lock: &gvt->scheduler.mmio_context_lock); |
106 | mutex_unlock(lock: &gvt->lock); |
107 | |
108 | /* In an ascending order by mmio offset. */ |
109 | list_sort(NULL, head: ¶m.diff_mmio_list, cmp: mmio_offset_compare); |
110 | |
111 | seq_printf(m: s, fmt: "%-8s %-8s %-8s %-8s\n" , "Offset" , "HW" , "vGPU" , "Diff" ); |
112 | list_for_each_entry_safe(node, next, ¶m.diff_mmio_list, node) { |
113 | u32 diff = node->preg ^ node->vreg; |
114 | |
115 | seq_printf(m: s, fmt: "%08x %08x %08x %*pbl\n" , |
116 | node->offset, node->preg, node->vreg, |
117 | 32, &diff); |
118 | list_del(entry: &node->node); |
119 | kfree(objp: node); |
120 | } |
121 | seq_printf(m: s, fmt: "Total: %d, Diff: %d\n" , param.total, param.diff); |
122 | return 0; |
123 | } |
124 | DEFINE_SHOW_ATTRIBUTE(vgpu_mmio_diff); |
125 | |
126 | static int |
127 | vgpu_scan_nonprivbb_get(void *data, u64 *val) |
128 | { |
129 | struct intel_vgpu *vgpu = (struct intel_vgpu *)data; |
130 | |
131 | *val = vgpu->scan_nonprivbb; |
132 | return 0; |
133 | } |
134 | |
135 | /* |
136 | * set/unset bit engine_id of vgpu->scan_nonprivbb to turn on/off scanning |
137 | * of non-privileged batch buffer. e.g. |
138 | * if vgpu->scan_nonprivbb=3, then it will scan non-privileged batch buffer |
139 | * on engine 0 and 1. |
140 | */ |
141 | static int |
142 | vgpu_scan_nonprivbb_set(void *data, u64 val) |
143 | { |
144 | struct intel_vgpu *vgpu = (struct intel_vgpu *)data; |
145 | |
146 | vgpu->scan_nonprivbb = val; |
147 | return 0; |
148 | } |
149 | |
150 | DEFINE_DEBUGFS_ATTRIBUTE(vgpu_scan_nonprivbb_fops, |
151 | vgpu_scan_nonprivbb_get, vgpu_scan_nonprivbb_set, |
152 | "0x%llx\n" ); |
153 | |
154 | static int vgpu_status_get(void *data, u64 *val) |
155 | { |
156 | struct intel_vgpu *vgpu = (struct intel_vgpu *)data; |
157 | |
158 | *val = 0; |
159 | |
160 | if (test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status)) |
161 | *val |= (1 << INTEL_VGPU_STATUS_ATTACHED); |
162 | if (test_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status)) |
163 | *val |= (1 << INTEL_VGPU_STATUS_ACTIVE); |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | DEFINE_DEBUGFS_ATTRIBUTE(vgpu_status_fops, vgpu_status_get, NULL, "0x%llx\n" ); |
169 | |
170 | /** |
171 | * intel_gvt_debugfs_add_vgpu - register debugfs entries for a vGPU |
172 | * @vgpu: a vGPU |
173 | */ |
174 | void intel_gvt_debugfs_add_vgpu(struct intel_vgpu *vgpu) |
175 | { |
176 | char name[16] = "" ; |
177 | |
178 | snprintf(buf: name, size: 16, fmt: "vgpu%d" , vgpu->id); |
179 | vgpu->debugfs = debugfs_create_dir(name, parent: vgpu->gvt->debugfs_root); |
180 | |
181 | debugfs_create_file(name: "mmio_diff" , mode: 0444, parent: vgpu->debugfs, data: vgpu, |
182 | fops: &vgpu_mmio_diff_fops); |
183 | debugfs_create_file_unsafe(name: "scan_nonprivbb" , mode: 0644, parent: vgpu->debugfs, data: vgpu, |
184 | fops: &vgpu_scan_nonprivbb_fops); |
185 | debugfs_create_file_unsafe(name: "status" , mode: 0644, parent: vgpu->debugfs, data: vgpu, |
186 | fops: &vgpu_status_fops); |
187 | } |
188 | |
189 | /** |
190 | * intel_gvt_debugfs_remove_vgpu - remove debugfs entries of a vGPU |
191 | * @vgpu: a vGPU |
192 | */ |
193 | void intel_gvt_debugfs_remove_vgpu(struct intel_vgpu *vgpu) |
194 | { |
195 | struct intel_gvt *gvt = vgpu->gvt; |
196 | struct drm_minor *minor = gvt->gt->i915->drm.primary; |
197 | |
198 | if (minor->debugfs_root && gvt->debugfs_root) { |
199 | debugfs_remove_recursive(dentry: vgpu->debugfs); |
200 | vgpu->debugfs = NULL; |
201 | } |
202 | } |
203 | |
204 | /** |
205 | * intel_gvt_debugfs_init - register gvt debugfs root entry |
206 | * @gvt: GVT device |
207 | */ |
208 | void intel_gvt_debugfs_init(struct intel_gvt *gvt) |
209 | { |
210 | struct drm_minor *minor = gvt->gt->i915->drm.primary; |
211 | |
212 | gvt->debugfs_root = debugfs_create_dir(name: "gvt" , parent: minor->debugfs_root); |
213 | |
214 | debugfs_create_ulong(name: "num_tracked_mmio" , mode: 0444, parent: gvt->debugfs_root, |
215 | value: &gvt->mmio.num_tracked_mmio); |
216 | } |
217 | |
218 | /** |
219 | * intel_gvt_debugfs_clean - remove debugfs entries |
220 | * @gvt: GVT device |
221 | */ |
222 | void intel_gvt_debugfs_clean(struct intel_gvt *gvt) |
223 | { |
224 | struct drm_minor *minor = gvt->gt->i915->drm.primary; |
225 | |
226 | if (minor->debugfs_root) { |
227 | debugfs_remove_recursive(dentry: gvt->debugfs_root); |
228 | gvt->debugfs_root = NULL; |
229 | } |
230 | } |
231 | |