static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size) { unsigned long first, last; int err, rw = 0; unsigned int flags = FOLL_FORCE; dma->direction = direction; switch (dma->direction) { case DMA_FROM_DEVICE: rw = READ; break; case DMA_TO_DEVICE: rw = WRITE; break; default: BUG(); } first = (data & PAGE_MASK) >> PAGE_SHIFT; last = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT; dma->offset = data & ~PAGE_MASK; dma->size = size; dma->nr_pages = last-first+1; dma->pages = kmalloc(dma->nr_pages * sizeof(struct page *), GFP_KERNEL); if (NULL == dma->pages) return -ENOMEM; if (rw == READ) flags |= FOLL_WRITE; dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n", data, size, dma->nr_pages); err = get_user_pages_longterm(data & PAGE_MASK, dma->nr_pages, flags, dma->pages, NULL); if (err != dma->nr_pages) { dma->nr_pages = (err >= 0) ? err : 0; dprintk(1, "get_user_pages_longterm: err=%d [%d]\n", err, dma->nr_pages); return err < 0 ? err : -EINVAL; } return 0; }
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; } switch (cmd) { case GUP_FAST_BENCHMARK: nr = get_user_pages_fast(addr, nr, gup->flags & 1, pages + i); break; case GUP_LONGTERM_BENCHMARK: nr = get_user_pages_longterm(addr, nr, gup->flags & 1, pages + i, NULL); break; case GUP_BENCHMARK: nr = get_user_pages(addr, nr, gup->flags & 1, pages + i, NULL); break; default: return -1; } if (nr <= 0) break; i += nr; } end_time = ktime_get(); gup->get_delta_usec = ktime_us_delta(end_time, start_time); gup->size = addr - gup->addr; start_time = ktime_get(); for (i = 0; i < nr_pages; i++) { if (!pages[i]) break; put_page(pages[i]); } end_time = ktime_get(); gup->put_delta_usec = ktime_us_delta(end_time, start_time); kvfree(pages); return 0; }