Blame view

mm/gup_benchmark.c 2.06 KB
64c349f4a   Kirill A. Shutemov   mm: add infrastru...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  #include <linux/kernel.h>
  #include <linux/mm.h>
  #include <linux/slab.h>
  #include <linux/uaccess.h>
  #include <linux/ktime.h>
  #include <linux/debugfs.h>
  
  #define GUP_FAST_BENCHMARK	_IOWR('g', 1, struct gup_benchmark)
  
  struct gup_benchmark {
  	__u64 delta_usec;
  	__u64 addr;
  	__u64 size;
  	__u32 nr_pages_per_call;
  	__u32 flags;
  };
  
  static int __gup_benchmark_ioctl(unsigned int cmd,
  		struct gup_benchmark *gup)
  {
  	ktime_t start_time, end_time;
518968645   YueHaibing   mm/gup_benchmark:...
22
23
  	unsigned long i, nr_pages, addr, next;
  	int nr;
64c349f4a   Kirill A. Shutemov   mm: add infrastru...
24
25
26
  	struct page **pages;
  
  	nr_pages = gup->size / PAGE_SIZE;
778e1cdd8   Kees Cook   treewide: kvzallo...
27
  	pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL);
64c349f4a   Kirill A. Shutemov   mm: add infrastru...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  	if (!pages)
  		return -ENOMEM;
  
  	i = 0;
  	nr = gup->nr_pages_per_call;
  	start_time = ktime_get();
  	for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
  		if (nr != gup->nr_pages_per_call)
  			break;
  
  		next = addr + nr * PAGE_SIZE;
  		if (next > gup->addr + gup->size) {
  			next = gup->addr + gup->size;
  			nr = (next - addr) / PAGE_SIZE;
  		}
  
  		nr = get_user_pages_fast(addr, nr, gup->flags & 1, pages + i);
09e35a4a1   Michael S. Tsirkin   mm/gup_benchmark:...
45
46
  		if (nr <= 0)
  			break;
64c349f4a   Kirill A. Shutemov   mm: add infrastru...
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
  		i += nr;
  	}
  	end_time = ktime_get();
  
  	gup->delta_usec = ktime_us_delta(end_time, start_time);
  	gup->size = addr - gup->addr;
  
  	for (i = 0; i < nr_pages; i++) {
  		if (!pages[i])
  			break;
  		put_page(pages[i]);
  	}
  
  	kvfree(pages);
  	return 0;
  }
  
  static long gup_benchmark_ioctl(struct file *filep, unsigned int cmd,
  		unsigned long arg)
  {
  	struct gup_benchmark gup;
  	int ret;
  
  	if (cmd != GUP_FAST_BENCHMARK)
  		return -EINVAL;
  
  	if (copy_from_user(&gup, (void __user *)arg, sizeof(gup)))
  		return -EFAULT;
  
  	ret = __gup_benchmark_ioctl(cmd, &gup);
  	if (ret)
  		return ret;
  
  	if (copy_to_user((void __user *)arg, &gup, sizeof(gup)))
  		return -EFAULT;
  
  	return 0;
  }
  
  static const struct file_operations gup_benchmark_fops = {
  	.open = nonseekable_open,
  	.unlocked_ioctl = gup_benchmark_ioctl,
  };
  
  static int gup_benchmark_init(void)
  {
  	void *ret;
  
  	ret = debugfs_create_file_unsafe("gup_benchmark", 0600, NULL, NULL,
  			&gup_benchmark_fops);
  	if (!ret)
  		pr_warn("Failed to create gup_benchmark in debugfs");
  
  	return 0;
  }
  
  late_initcall(gup_benchmark_init);