blob: 85d89acd9205cca4de71743b980a4b9e0f958787 [file] [log] [blame]
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001/*
2 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#define pr_fmt(fmt) "iommu-debug: %s: " fmt, __func__
16
17#include <linux/debugfs.h>
18#include <linux/device.h>
19#include <linux/iommu.h>
20#include <linux/of.h>
21#include <linux/platform_device.h>
22#include <linux/slab.h>
23#include <linux/module.h>
24
25static struct dentry *debugfs_top_dir;
26
27#ifdef CONFIG_IOMMU_DEBUG_TRACKING
28
29static DEFINE_MUTEX(iommu_debug_attachments_lock);
30static LIST_HEAD(iommu_debug_attachments);
31static struct dentry *debugfs_attachments_dir;
32
33struct iommu_debug_attachment {
34 struct iommu_domain *domain;
35 struct device *dev;
36 struct dentry *dentry;
37 struct list_head list;
38};
39
40static int iommu_debug_attachment_show(struct seq_file *s, void *ignored)
41{
42 struct iommu_debug_attachment *attach = s->private;
43
44 seq_printf(s, "Domain: 0x%p\n", attach->domain);
45 return 0;
46}
47
48static int iommu_debug_attachment_open(struct inode *inode, struct file *file)
49{
50 return single_open(file, iommu_debug_attachment_show, inode->i_private);
51}
52
53static const struct file_operations iommu_debug_attachment_fops = {
54 .open = iommu_debug_attachment_open,
55 .read = seq_read,
56 .llseek = seq_lseek,
57 .release = single_release,
58};
59
60void iommu_debug_attach_device(struct iommu_domain *domain,
61 struct device *dev)
62{
63 struct iommu_debug_attachment *attach;
Mitchel Humpherys876e2be2015-07-10 11:56:56 -070064 char *attach_name;
65 uuid_le uuid;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070066
67 mutex_lock(&iommu_debug_attachments_lock);
68
Mitchel Humpherys876e2be2015-07-10 11:56:56 -070069 uuid_le_gen(&uuid);
70 attach_name = kasprintf(GFP_KERNEL, "%s-%pUl", dev_name(dev), uuid.b);
71 if (!attach_name)
72 goto unlock;
73
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070074 attach = kmalloc(sizeof(*attach), GFP_KERNEL);
75 if (!attach)
Mitchel Humpherys876e2be2015-07-10 11:56:56 -070076 goto free_attach_name;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070077
78 attach->domain = domain;
79 attach->dev = dev;
80
81 attach->dentry = debugfs_create_file(
Mitchel Humpherys876e2be2015-07-10 11:56:56 -070082 attach_name, S_IRUSR, debugfs_attachments_dir, attach,
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070083 &iommu_debug_attachment_fops);
84 if (!attach->dentry) {
85 pr_err("Couldn't create iommu/attachments/%s debugfs file for domain 0x%p\n",
Mitchel Humpherys876e2be2015-07-10 11:56:56 -070086 attach_name, domain);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070087 kfree(attach);
Mitchel Humpherys876e2be2015-07-10 11:56:56 -070088 goto free_attach_name;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070089 }
90
91 list_add(&attach->list, &iommu_debug_attachments);
Mitchel Humpherys876e2be2015-07-10 11:56:56 -070092free_attach_name:
93 kfree(attach_name);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070094unlock:
95 mutex_unlock(&iommu_debug_attachments_lock);
96}
97
98void iommu_debug_detach_device(struct iommu_domain *domain,
99 struct device *dev)
100{
101 struct iommu_debug_attachment *it;
102
103 mutex_lock(&iommu_debug_attachments_lock);
104 list_for_each_entry(it, &iommu_debug_attachments, list)
105 if (it->domain == domain && it->dev == dev)
106 break;
107
108 if (&it->list == &iommu_debug_attachments) {
109 WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s",
110 domain, dev_name(dev));
111 } else {
112 list_del(&it->list);
113 debugfs_remove_recursive(it->dentry);
114 kfree(it);
115 }
116 mutex_unlock(&iommu_debug_attachments_lock);
117}
118
119static int iommu_debug_init_tracking(void)
120{
121 debugfs_attachments_dir = debugfs_create_dir("attachments",
122 debugfs_top_dir);
123 if (!debugfs_attachments_dir) {
124 pr_err("Couldn't create iommu/attachments debugfs directory\n");
125 return -ENODEV;
126 }
127
128 return 0;
129}
130#else
131static inline int iommu_debug_init_tracking(void) { return 0; }
132#endif
133
134#ifdef CONFIG_IOMMU_TESTS
135
136static LIST_HEAD(iommu_debug_devices);
137static struct dentry *debugfs_tests_dir;
138
139struct iommu_debug_device {
140 struct device *dev;
141 struct list_head list;
142};
143
144static int iommu_debug_build_phoney_sg_table(struct device *dev,
145 struct sg_table *table,
146 unsigned long total_size,
147 unsigned long chunk_size)
148{
149 unsigned long nents = total_size / chunk_size;
150 struct scatterlist *sg;
151 int i;
152 struct page *page;
153
154 if (!IS_ALIGNED(total_size, PAGE_SIZE))
155 return -EINVAL;
156 if (!IS_ALIGNED(total_size, chunk_size))
157 return -EINVAL;
158 if (sg_alloc_table(table, nents, GFP_KERNEL))
159 return -EINVAL;
160 page = alloc_pages(GFP_KERNEL, get_order(chunk_size));
161 if (!page)
162 goto free_table;
163
164 /* all the same page... why not. */
165 for_each_sg(table->sgl, sg, table->nents, i)
166 sg_set_page(sg, page, chunk_size, 0);
167
168 return 0;
169
170free_table:
171 sg_free_table(table);
172 return -ENOMEM;
173}
174
175static void iommu_debug_destroy_phoney_sg_table(struct device *dev,
176 struct sg_table *table,
177 unsigned long chunk_size)
178{
179 __free_pages(sg_page(table->sgl), get_order(chunk_size));
180 sg_free_table(table);
181}
182
183static const char * const _size_to_string(unsigned long size)
184{
185 switch (size) {
186 case SZ_4K:
187 return "4K";
188 case SZ_64K:
189 return "64K";
190 case SZ_2M:
191 return "2M";
192 case SZ_1M * 12:
193 return "12M";
194 case SZ_1M * 20:
195 return "20M";
196 }
197 return "unknown size, please add to _size_to_string";
198}
199
200static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev)
201{
202 unsigned long sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12,
203 SZ_1M * 20, 0 };
204 unsigned long *sz;
205 struct iommu_domain *domain;
206 unsigned long iova = 0x10000;
207 phys_addr_t paddr = 0xa000;
208
209 domain = iommu_domain_alloc(&platform_bus_type);
210 if (!domain) {
211 seq_puts(s, "Couldn't allocate domain\n");
212 return;
213 }
214
215 if (iommu_attach_device(domain, dev)) {
216 seq_puts(s,
217 "Couldn't attach new domain to device. Is it already attached?\n");
218 goto out_domain_free;
219 }
220
221 seq_printf(s, "%8s %15s %12s\n", "size", "iommu_map", "iommu_unmap");
222 for (sz = sizes; *sz; ++sz) {
223 unsigned long size = *sz;
224 size_t unmapped;
225 s64 map_elapsed_us, unmap_elapsed_us;
226 struct timespec tbefore, tafter, diff;
227
228 getnstimeofday(&tbefore);
229 if (iommu_map(domain, iova, paddr, size,
230 IOMMU_READ | IOMMU_WRITE)) {
231 seq_puts(s, "Failed to map\n");
232 continue;
233 }
234 getnstimeofday(&tafter);
235 diff = timespec_sub(tafter, tbefore);
236 map_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
237
238 getnstimeofday(&tbefore);
239 unmapped = iommu_unmap(domain, iova, size);
240 if (unmapped != size) {
241 seq_printf(s, "Only unmapped %zx instead of %zx\n",
242 unmapped, size);
243 continue;
244 }
245 getnstimeofday(&tafter);
246 diff = timespec_sub(tafter, tbefore);
247 unmap_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
248
249 seq_printf(s, "%8s %12lld us %9lld us\n", _size_to_string(size),
250 map_elapsed_us, unmap_elapsed_us);
251 }
252
253 seq_putc(s, '\n');
254 seq_printf(s, "%8s %15s %12s\n", "size", "iommu_map_sg", "iommu_unmap");
255 for (sz = sizes; *sz; ++sz) {
256 unsigned long size = *sz;
257 size_t unmapped;
258 s64 map_elapsed_us, unmap_elapsed_us;
259 struct timespec tbefore, tafter, diff;
260 struct sg_table table;
261 unsigned long chunk_size = SZ_4K;
262
263 if (iommu_debug_build_phoney_sg_table(dev, &table, size,
264 chunk_size)) {
265 seq_puts(s,
266 "couldn't build phoney sg table! bailing...\n");
267 goto out_detach;
268 }
269
270 getnstimeofday(&tbefore);
271 if (iommu_map_sg(domain, iova, table.sgl, table.nents,
272 IOMMU_READ | IOMMU_WRITE) != size) {
273 seq_puts(s, "Failed to map_sg\n");
274 goto next;
275 }
276 getnstimeofday(&tafter);
277 diff = timespec_sub(tafter, tbefore);
278 map_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
279
280 getnstimeofday(&tbefore);
281 unmapped = iommu_unmap(domain, iova, size);
282 if (unmapped != size) {
283 seq_printf(s, "Only unmapped %zx instead of %zx\n",
284 unmapped, size);
285 goto next;
286 }
287 getnstimeofday(&tafter);
288 diff = timespec_sub(tafter, tbefore);
289 unmap_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
290
291 seq_printf(s, "%8s %12lld us %9lld us\n", _size_to_string(size),
292 map_elapsed_us, unmap_elapsed_us);
293
294next:
295 iommu_debug_destroy_phoney_sg_table(dev, &table, chunk_size);
296 }
297
298out_detach:
299 iommu_detach_device(domain, dev);
300out_domain_free:
301 iommu_domain_free(domain);
302}
303
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700304static int iommu_debug_profiling_show(struct seq_file *s, void *ignored)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700305{
306 struct iommu_debug_device *ddev = s->private;
307
308 iommu_debug_device_profiling(s, ddev->dev);
309
310 return 0;
311}
312
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700313static int iommu_debug_profiling_open(struct inode *inode, struct file *file)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700314{
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700315 return single_open(file, iommu_debug_profiling_show, inode->i_private);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700316}
317
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700318static const struct file_operations iommu_debug_profiling_fops = {
319 .open = iommu_debug_profiling_open,
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700320 .read = seq_read,
321 .llseek = seq_lseek,
322 .release = single_release,
323};
324
325/*
326 * The following will only work for drivers that implement the generic
327 * device tree bindings described in
328 * Documentation/devicetree/bindings/iommu/iommu.txt
329 */
330static int snarf_iommu_devices(struct device *dev, void *ignored)
331{
332 struct iommu_debug_device *ddev;
333 struct dentry *dir, *profiling_dentry;
334
335 if (!of_find_property(dev->of_node, "iommus", NULL))
336 return 0;
337
338 ddev = kmalloc(sizeof(*ddev), GFP_KERNEL);
339 if (!ddev)
340 return -ENODEV;
341 ddev->dev = dev;
342 dir = debugfs_create_dir(dev_name(dev), debugfs_tests_dir);
343 if (!dir) {
344 pr_err("Couldn't create iommu/devices/%s debugfs dir\n",
345 dev_name(dev));
346 goto err;
347 }
348 profiling_dentry = debugfs_create_file("profiling", S_IRUSR, dir, ddev,
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700349 &iommu_debug_profiling_fops);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700350 if (!profiling_dentry) {
351 pr_err("Couldn't create iommu/devices/%s/profiling debugfs file\n",
352 dev_name(dev));
353 goto err_rmdir;
354 }
355
356 list_add(&ddev->list, &iommu_debug_devices);
357 return 0;
358
359err_rmdir:
360 debugfs_remove_recursive(dir);
361err:
362 kfree(ddev);
363 return 0;
364}
365
366static int iommu_debug_init_tests(void)
367{
368 debugfs_tests_dir = debugfs_create_dir("tests",
369 debugfs_top_dir);
370 if (!debugfs_tests_dir) {
371 pr_err("Couldn't create iommu/tests debugfs directory\n");
372 return -ENODEV;
373 }
374
375 return bus_for_each_dev(&platform_bus_type, NULL, NULL,
376 snarf_iommu_devices);
377}
378#else
379static inline int iommu_debug_init_tests(void) { return 0; }
380#endif
381
382static int iommu_debug_init(void)
383{
384 debugfs_top_dir = debugfs_create_dir("iommu", NULL);
385 if (!debugfs_top_dir) {
386 pr_err("Couldn't create iommu debugfs directory\n");
387 return -ENODEV;
388 }
389
390 if (iommu_debug_init_tracking())
391 goto err;
392
393 if (iommu_debug_init_tests())
394 goto err;
395
396 return 0;
397err:
398 debugfs_remove_recursive(debugfs_top_dir);
399 return -ENODEV;
400}
401
402static void iommu_debug_exit(void)
403{
404 debugfs_remove_recursive(debugfs_top_dir);
405}
406
407module_init(iommu_debug_init);
408module_exit(iommu_debug_exit);