mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-06 20:53:54 +08:00
a9bed1e1c2
Therefore, some minor cleanup and improvements are in order: 1. Rename the other items appropriately. 2. Stop reporting timing information on the non-benchmark items. It's still being recorded and is available, but there's no point in cluttering up the report with data that no one reasonably needs to check. 3. Don't do iterations, for non-benchmark items. 4. Print out a shorter, more appropriate report for the non-benchmark tests. 5. Add the command that was run, to the report. This really helps, as there are quite a lot of options now. 6. Use a larger integer type for cmd, now that it's being compared Otherwise it doesn't work, because in this case cmd is about 3 billion, which is the perfect size for problems with signed vs unsigned int. Link: https://lkml.kernel.org/r/20201026064021.3545418-6-jhubbard@nvidia.com Signed-off-by: John Hubbard <jhubbard@nvidia.com> Cc: Jérôme Glisse <jglisse@redhat.com> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Ralph Campbell <rcampbell@nvidia.com> Cc: Shuah Khan <shuah@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
196 lines
4.0 KiB
C
196 lines
4.0 KiB
C
#include <linux/kernel.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/ktime.h>
|
|
#include <linux/debugfs.h>
|
|
#include "gup_test.h"
|
|
|
|
static void put_back_pages(unsigned int cmd, struct page **pages,
|
|
unsigned long nr_pages)
|
|
{
|
|
unsigned long i;
|
|
|
|
switch (cmd) {
|
|
case GUP_FAST_BENCHMARK:
|
|
case GUP_BASIC_TEST:
|
|
for (i = 0; i < nr_pages; i++)
|
|
put_page(pages[i]);
|
|
break;
|
|
|
|
case PIN_FAST_BENCHMARK:
|
|
case PIN_BASIC_TEST:
|
|
case PIN_LONGTERM_BENCHMARK:
|
|
unpin_user_pages(pages, nr_pages);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void verify_dma_pinned(unsigned int cmd, struct page **pages,
|
|
unsigned long nr_pages)
|
|
{
|
|
unsigned long i;
|
|
struct page *page;
|
|
|
|
switch (cmd) {
|
|
case PIN_FAST_BENCHMARK:
|
|
case PIN_BASIC_TEST:
|
|
case PIN_LONGTERM_BENCHMARK:
|
|
for (i = 0; i < nr_pages; i++) {
|
|
page = pages[i];
|
|
if (WARN(!page_maybe_dma_pinned(page),
|
|
"pages[%lu] is NOT dma-pinned\n", i)) {
|
|
|
|
dump_page(page, "gup_test failure");
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int __gup_test_ioctl(unsigned int cmd,
|
|
struct gup_test *gup)
|
|
{
|
|
ktime_t start_time, end_time;
|
|
unsigned long i, nr_pages, addr, next;
|
|
int nr;
|
|
struct page **pages;
|
|
int ret = 0;
|
|
bool needs_mmap_lock =
|
|
cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK;
|
|
|
|
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;
|
|
|
|
if (needs_mmap_lock && mmap_read_lock_killable(current->mm)) {
|
|
ret = -EINTR;
|
|
goto free_pages;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* Filter out most gup flags: only allow a tiny subset here: */
|
|
gup->flags &= FOLL_WRITE;
|
|
|
|
switch (cmd) {
|
|
case GUP_FAST_BENCHMARK:
|
|
nr = get_user_pages_fast(addr, nr, gup->flags,
|
|
pages + i);
|
|
break;
|
|
case GUP_BASIC_TEST:
|
|
nr = get_user_pages(addr, nr, gup->flags, pages + i,
|
|
NULL);
|
|
break;
|
|
case PIN_FAST_BENCHMARK:
|
|
nr = pin_user_pages_fast(addr, nr, gup->flags,
|
|
pages + i);
|
|
break;
|
|
case PIN_BASIC_TEST:
|
|
nr = pin_user_pages(addr, nr, gup->flags, pages + i,
|
|
NULL);
|
|
break;
|
|
case PIN_LONGTERM_BENCHMARK:
|
|
nr = pin_user_pages(addr, nr,
|
|
gup->flags | FOLL_LONGTERM,
|
|
pages + i, NULL);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto unlock;
|
|
}
|
|
|
|
if (nr <= 0)
|
|
break;
|
|
i += nr;
|
|
}
|
|
end_time = ktime_get();
|
|
|
|
/* Shifting the meaning of nr_pages: now it is actual number pinned: */
|
|
nr_pages = i;
|
|
|
|
gup->get_delta_usec = ktime_us_delta(end_time, start_time);
|
|
gup->size = addr - gup->addr;
|
|
|
|
/*
|
|
* Take an un-benchmark-timed moment to verify DMA pinned
|
|
* state: print a warning if any non-dma-pinned pages are found:
|
|
*/
|
|
verify_dma_pinned(cmd, pages, nr_pages);
|
|
|
|
start_time = ktime_get();
|
|
|
|
put_back_pages(cmd, pages, nr_pages);
|
|
|
|
end_time = ktime_get();
|
|
gup->put_delta_usec = ktime_us_delta(end_time, start_time);
|
|
|
|
unlock:
|
|
if (needs_mmap_lock)
|
|
mmap_read_unlock(current->mm);
|
|
free_pages:
|
|
kvfree(pages);
|
|
return ret;
|
|
}
|
|
|
|
static long gup_test_ioctl(struct file *filep, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
struct gup_test gup;
|
|
int ret;
|
|
|
|
switch (cmd) {
|
|
case GUP_FAST_BENCHMARK:
|
|
case PIN_FAST_BENCHMARK:
|
|
case PIN_LONGTERM_BENCHMARK:
|
|
case GUP_BASIC_TEST:
|
|
case PIN_BASIC_TEST:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (copy_from_user(&gup, (void __user *)arg, sizeof(gup)))
|
|
return -EFAULT;
|
|
|
|
ret = __gup_test_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_test_fops = {
|
|
.open = nonseekable_open,
|
|
.unlocked_ioctl = gup_test_ioctl,
|
|
};
|
|
|
|
static int gup_test_init(void)
|
|
{
|
|
debugfs_create_file_unsafe("gup_test", 0600, NULL, NULL,
|
|
&gup_test_fops);
|
|
|
|
return 0;
|
|
}
|
|
|
|
late_initcall(gup_test_init);
|