linux/mm/gup_benchmark.c
<<
>>
Prefs
   1#include <linux/kernel.h>
   2#include <linux/mm.h>
   3#include <linux/slab.h>
   4#include <linux/uaccess.h>
   5#include <linux/ktime.h>
   6#include <linux/debugfs.h>
   7
   8#define GUP_FAST_BENCHMARK      _IOWR('g', 1, struct gup_benchmark)
   9#define GUP_LONGTERM_BENCHMARK  _IOWR('g', 2, struct gup_benchmark)
  10#define GUP_BENCHMARK           _IOWR('g', 3, struct gup_benchmark)
  11
  12struct gup_benchmark {
  13        __u64 get_delta_usec;
  14        __u64 put_delta_usec;
  15        __u64 addr;
  16        __u64 size;
  17        __u32 nr_pages_per_call;
  18        __u32 flags;
  19        __u64 expansion[10];    /* For future use */
  20};
  21
  22static int __gup_benchmark_ioctl(unsigned int cmd,
  23                struct gup_benchmark *gup)
  24{
  25        ktime_t start_time, end_time;
  26        unsigned long i, nr_pages, addr, next;
  27        int nr;
  28        struct page **pages;
  29
  30        if (gup->size > ULONG_MAX)
  31                return -EINVAL;
  32
  33        nr_pages = gup->size / PAGE_SIZE;
  34        pages = kvcalloc(nr_pages, sizeof(void *), GFP_KERNEL);
  35        if (!pages)
  36                return -ENOMEM;
  37
  38        i = 0;
  39        nr = gup->nr_pages_per_call;
  40        start_time = ktime_get();
  41        for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
  42                if (nr != gup->nr_pages_per_call)
  43                        break;
  44
  45                next = addr + nr * PAGE_SIZE;
  46                if (next > gup->addr + gup->size) {
  47                        next = gup->addr + gup->size;
  48                        nr = (next - addr) / PAGE_SIZE;
  49                }
  50
  51                switch (cmd) {
  52                case GUP_FAST_BENCHMARK:
  53                        nr = get_user_pages_fast(addr, nr, gup->flags & 1,
  54                                                 pages + i);
  55                        break;
  56                case GUP_LONGTERM_BENCHMARK:
  57                        nr = get_user_pages(addr, nr,
  58                                            (gup->flags & 1) | FOLL_LONGTERM,
  59                                            pages + i, NULL);
  60                        break;
  61                case GUP_BENCHMARK:
  62                        nr = get_user_pages(addr, nr, gup->flags & 1, pages + i,
  63                                            NULL);
  64                        break;
  65                default:
  66                        return -1;
  67                }
  68
  69                if (nr <= 0)
  70                        break;
  71                i += nr;
  72        }
  73        end_time = ktime_get();
  74
  75        gup->get_delta_usec = ktime_us_delta(end_time, start_time);
  76        gup->size = addr - gup->addr;
  77
  78        start_time = ktime_get();
  79        for (i = 0; i < nr_pages; i++) {
  80                if (!pages[i])
  81                        break;
  82                put_page(pages[i]);
  83        }
  84        end_time = ktime_get();
  85        gup->put_delta_usec = ktime_us_delta(end_time, start_time);
  86
  87        kvfree(pages);
  88        return 0;
  89}
  90
  91static long gup_benchmark_ioctl(struct file *filep, unsigned int cmd,
  92                unsigned long arg)
  93{
  94        struct gup_benchmark gup;
  95        int ret;
  96
  97        switch (cmd) {
  98        case GUP_FAST_BENCHMARK:
  99        case GUP_LONGTERM_BENCHMARK:
 100        case GUP_BENCHMARK:
 101                break;
 102        default:
 103                return -EINVAL;
 104        }
 105
 106        if (copy_from_user(&gup, (void __user *)arg, sizeof(gup)))
 107                return -EFAULT;
 108
 109        ret = __gup_benchmark_ioctl(cmd, &gup);
 110        if (ret)
 111                return ret;
 112
 113        if (copy_to_user((void __user *)arg, &gup, sizeof(gup)))
 114                return -EFAULT;
 115
 116        return 0;
 117}
 118
 119static const struct file_operations gup_benchmark_fops = {
 120        .open = nonseekable_open,
 121        .unlocked_ioctl = gup_benchmark_ioctl,
 122};
 123
 124static int gup_benchmark_init(void)
 125{
 126        debugfs_create_file_unsafe("gup_benchmark", 0600, NULL, NULL,
 127                                   &gup_benchmark_fops);
 128
 129        return 0;
 130}
 131
 132late_initcall(gup_benchmark_init);
 133