hc
2023-11-06 15ade055295d13f95d49e3d99b09f3bbfb4a43e7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
104
105
106
#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;
   unsigned long i, nr_pages, addr, next;
   int nr;
   struct page **pages;
 
   if (gup->size > ULONG_MAX)
       return -EINVAL;
 
   nr_pages = gup->size / PAGE_SIZE;
   pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL);
   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);
       if (nr <= 0)
           break;
       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);