.. | .. |
---|
6 | 6 | #include <linux/debugfs.h> |
---|
7 | 7 | |
---|
8 | 8 | #define GUP_FAST_BENCHMARK _IOWR('g', 1, struct gup_benchmark) |
---|
| 9 | +#define GUP_BENCHMARK _IOWR('g', 2, struct gup_benchmark) |
---|
| 10 | +#define PIN_FAST_BENCHMARK _IOWR('g', 3, struct gup_benchmark) |
---|
| 11 | +#define PIN_BENCHMARK _IOWR('g', 4, struct gup_benchmark) |
---|
| 12 | +#define PIN_LONGTERM_BENCHMARK _IOWR('g', 5, struct gup_benchmark) |
---|
9 | 13 | |
---|
10 | 14 | struct gup_benchmark { |
---|
11 | | - __u64 delta_usec; |
---|
| 15 | + __u64 get_delta_usec; |
---|
| 16 | + __u64 put_delta_usec; |
---|
12 | 17 | __u64 addr; |
---|
13 | 18 | __u64 size; |
---|
14 | 19 | __u32 nr_pages_per_call; |
---|
15 | 20 | __u32 flags; |
---|
| 21 | + __u64 expansion[10]; /* For future use */ |
---|
16 | 22 | }; |
---|
| 23 | + |
---|
| 24 | +static void put_back_pages(unsigned int cmd, struct page **pages, |
---|
| 25 | + unsigned long nr_pages) |
---|
| 26 | +{ |
---|
| 27 | + unsigned long i; |
---|
| 28 | + |
---|
| 29 | + switch (cmd) { |
---|
| 30 | + case GUP_FAST_BENCHMARK: |
---|
| 31 | + case GUP_BENCHMARK: |
---|
| 32 | + for (i = 0; i < nr_pages; i++) |
---|
| 33 | + put_page(pages[i]); |
---|
| 34 | + break; |
---|
| 35 | + |
---|
| 36 | + case PIN_FAST_BENCHMARK: |
---|
| 37 | + case PIN_BENCHMARK: |
---|
| 38 | + case PIN_LONGTERM_BENCHMARK: |
---|
| 39 | + unpin_user_pages(pages, nr_pages); |
---|
| 40 | + break; |
---|
| 41 | + } |
---|
| 42 | +} |
---|
| 43 | + |
---|
| 44 | +static void verify_dma_pinned(unsigned int cmd, struct page **pages, |
---|
| 45 | + unsigned long nr_pages) |
---|
| 46 | +{ |
---|
| 47 | + unsigned long i; |
---|
| 48 | + struct page *page; |
---|
| 49 | + |
---|
| 50 | + switch (cmd) { |
---|
| 51 | + case PIN_FAST_BENCHMARK: |
---|
| 52 | + case PIN_BENCHMARK: |
---|
| 53 | + case PIN_LONGTERM_BENCHMARK: |
---|
| 54 | + for (i = 0; i < nr_pages; i++) { |
---|
| 55 | + page = pages[i]; |
---|
| 56 | + if (WARN(!page_maybe_dma_pinned(page), |
---|
| 57 | + "pages[%lu] is NOT dma-pinned\n", i)) { |
---|
| 58 | + |
---|
| 59 | + dump_page(page, "gup_benchmark failure"); |
---|
| 60 | + break; |
---|
| 61 | + } |
---|
| 62 | + } |
---|
| 63 | + break; |
---|
| 64 | + } |
---|
| 65 | +} |
---|
17 | 66 | |
---|
18 | 67 | static int __gup_benchmark_ioctl(unsigned int cmd, |
---|
19 | 68 | struct gup_benchmark *gup) |
---|
.. | .. |
---|
22 | 71 | unsigned long i, nr_pages, addr, next; |
---|
23 | 72 | int nr; |
---|
24 | 73 | struct page **pages; |
---|
| 74 | + int ret = 0; |
---|
| 75 | + bool needs_mmap_lock = |
---|
| 76 | + cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK; |
---|
25 | 77 | |
---|
26 | 78 | if (gup->size > ULONG_MAX) |
---|
27 | 79 | return -EINVAL; |
---|
.. | .. |
---|
30 | 82 | pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL); |
---|
31 | 83 | if (!pages) |
---|
32 | 84 | return -ENOMEM; |
---|
| 85 | + |
---|
| 86 | + if (needs_mmap_lock && mmap_read_lock_killable(current->mm)) { |
---|
| 87 | + ret = -EINTR; |
---|
| 88 | + goto free_pages; |
---|
| 89 | + } |
---|
33 | 90 | |
---|
34 | 91 | i = 0; |
---|
35 | 92 | nr = gup->nr_pages_per_call; |
---|
.. | .. |
---|
44 | 101 | nr = (next - addr) / PAGE_SIZE; |
---|
45 | 102 | } |
---|
46 | 103 | |
---|
47 | | - nr = get_user_pages_fast(addr, nr, gup->flags & 1, pages + i); |
---|
| 104 | + /* Filter out most gup flags: only allow a tiny subset here: */ |
---|
| 105 | + gup->flags &= FOLL_WRITE; |
---|
| 106 | + |
---|
| 107 | + switch (cmd) { |
---|
| 108 | + case GUP_FAST_BENCHMARK: |
---|
| 109 | + nr = get_user_pages_fast(addr, nr, gup->flags, |
---|
| 110 | + pages + i); |
---|
| 111 | + break; |
---|
| 112 | + case GUP_BENCHMARK: |
---|
| 113 | + nr = get_user_pages(addr, nr, gup->flags, pages + i, |
---|
| 114 | + NULL); |
---|
| 115 | + break; |
---|
| 116 | + case PIN_FAST_BENCHMARK: |
---|
| 117 | + nr = pin_user_pages_fast(addr, nr, gup->flags, |
---|
| 118 | + pages + i); |
---|
| 119 | + break; |
---|
| 120 | + case PIN_BENCHMARK: |
---|
| 121 | + nr = pin_user_pages(addr, nr, gup->flags, pages + i, |
---|
| 122 | + NULL); |
---|
| 123 | + break; |
---|
| 124 | + case PIN_LONGTERM_BENCHMARK: |
---|
| 125 | + nr = pin_user_pages(addr, nr, |
---|
| 126 | + gup->flags | FOLL_LONGTERM, |
---|
| 127 | + pages + i, NULL); |
---|
| 128 | + break; |
---|
| 129 | + default: |
---|
| 130 | + ret = -EINVAL; |
---|
| 131 | + goto unlock; |
---|
| 132 | + } |
---|
| 133 | + |
---|
48 | 134 | if (nr <= 0) |
---|
49 | 135 | break; |
---|
50 | 136 | i += nr; |
---|
51 | 137 | } |
---|
52 | 138 | end_time = ktime_get(); |
---|
53 | 139 | |
---|
54 | | - gup->delta_usec = ktime_us_delta(end_time, start_time); |
---|
| 140 | + /* Shifting the meaning of nr_pages: now it is actual number pinned: */ |
---|
| 141 | + nr_pages = i; |
---|
| 142 | + |
---|
| 143 | + gup->get_delta_usec = ktime_us_delta(end_time, start_time); |
---|
55 | 144 | gup->size = addr - gup->addr; |
---|
56 | 145 | |
---|
57 | | - for (i = 0; i < nr_pages; i++) { |
---|
58 | | - if (!pages[i]) |
---|
59 | | - break; |
---|
60 | | - put_page(pages[i]); |
---|
61 | | - } |
---|
| 146 | + /* |
---|
| 147 | + * Take an un-benchmark-timed moment to verify DMA pinned |
---|
| 148 | + * state: print a warning if any non-dma-pinned pages are found: |
---|
| 149 | + */ |
---|
| 150 | + verify_dma_pinned(cmd, pages, nr_pages); |
---|
62 | 151 | |
---|
| 152 | + start_time = ktime_get(); |
---|
| 153 | + |
---|
| 154 | + put_back_pages(cmd, pages, nr_pages); |
---|
| 155 | + |
---|
| 156 | + end_time = ktime_get(); |
---|
| 157 | + gup->put_delta_usec = ktime_us_delta(end_time, start_time); |
---|
| 158 | + |
---|
| 159 | +unlock: |
---|
| 160 | + if (needs_mmap_lock) |
---|
| 161 | + mmap_read_unlock(current->mm); |
---|
| 162 | +free_pages: |
---|
63 | 163 | kvfree(pages); |
---|
64 | | - return 0; |
---|
| 164 | + return ret; |
---|
65 | 165 | } |
---|
66 | 166 | |
---|
67 | 167 | static long gup_benchmark_ioctl(struct file *filep, unsigned int cmd, |
---|
.. | .. |
---|
70 | 170 | struct gup_benchmark gup; |
---|
71 | 171 | int ret; |
---|
72 | 172 | |
---|
73 | | - if (cmd != GUP_FAST_BENCHMARK) |
---|
| 173 | + switch (cmd) { |
---|
| 174 | + case GUP_FAST_BENCHMARK: |
---|
| 175 | + case GUP_BENCHMARK: |
---|
| 176 | + case PIN_FAST_BENCHMARK: |
---|
| 177 | + case PIN_BENCHMARK: |
---|
| 178 | + case PIN_LONGTERM_BENCHMARK: |
---|
| 179 | + break; |
---|
| 180 | + default: |
---|
74 | 181 | return -EINVAL; |
---|
| 182 | + } |
---|
75 | 183 | |
---|
76 | 184 | if (copy_from_user(&gup, (void __user *)arg, sizeof(gup))) |
---|
77 | 185 | return -EFAULT; |
---|
.. | .. |
---|
93 | 201 | |
---|
94 | 202 | static int gup_benchmark_init(void) |
---|
95 | 203 | { |
---|
96 | | - void *ret; |
---|
97 | | - |
---|
98 | | - ret = debugfs_create_file_unsafe("gup_benchmark", 0600, NULL, NULL, |
---|
99 | | - &gup_benchmark_fops); |
---|
100 | | - if (!ret) |
---|
101 | | - pr_warn("Failed to create gup_benchmark in debugfs"); |
---|
| 204 | + debugfs_create_file_unsafe("gup_benchmark", 0600, NULL, NULL, |
---|
| 205 | + &gup_benchmark_fops); |
---|
102 | 206 | |
---|
103 | 207 | return 0; |
---|
104 | 208 | } |
---|