Skip to content

Commit

Permalink
Merge tag 'topic/iomem-mmap-vs-gup-2021-02-22' of git://anongit.freed…
Browse files Browse the repository at this point in the history
…esktop.org/drm/drm

Pull follow_pfn() updates from Daniel Vetter:
 "Fixes around VM_FPNMAP and follow_pfn:

   - replace mm/frame_vector.c by get_user_pages in misc/habana and
     drm/exynos drivers, then move that into media as it's sole user

   - close race in generic_access_phys

   - s390 pci ioctl fix of this series landed in 5.11 already

   - properly revoke iomem mappings (/dev/mem, pci files)"

* tag 'topic/iomem-mmap-vs-gup-2021-02-22' of git://anongit.freedesktop.org/drm/drm:
  PCI: Revoke mappings like devmem
  PCI: Also set up legacy files only after sysfs init
  sysfs: Support zapping of binary attr mmaps
  resource: Move devmem revoke code to resource framework
  /dev/mem: Only set filp->f_mapping
  PCI: Obey iomem restrictions for procfs mmap
  mm: Close race in generic_access_phys
  media: videobuf2: Move frame_vector into media subsystem
  mm/frame-vector: Use FOLL_LONGTERM
  misc/habana: Use FOLL_LONGTERM for userptr
  misc/habana: Stop using frame_vector helpers
  drm/exynos: Use FOLL_LONGTERM for g2d cmdlists
  drm/exynos: Stop using frame_vector helpers
  • Loading branch information
Linus Torvalds committed Feb 23, 2021
2 parents 4b5f925 + 636b21b commit e913a8c
Show file tree
Hide file tree
Showing 23 changed files with 287 additions and 245 deletions.
86 changes: 1 addition & 85 deletions drivers/char/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@
#include <linux/uio.h>
#include <linux/uaccess.h>
#include <linux/security.h>
#include <linux/pseudo_fs.h>
#include <uapi/linux/magic.h>
#include <linux/mount.h>

#ifdef CONFIG_IA64
# include <linux/efi.h>
Expand Down Expand Up @@ -829,42 +826,6 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
return ret;
}

static struct inode *devmem_inode;

#ifdef CONFIG_IO_STRICT_DEVMEM
void revoke_devmem(struct resource *res)
{
/* pairs with smp_store_release() in devmem_init_inode() */
struct inode *inode = smp_load_acquire(&devmem_inode);

/*
* Check that the initialization has completed. Losing the race
* is ok because it means drivers are claiming resources before
* the fs_initcall level of init and prevent /dev/mem from
* establishing mappings.
*/
if (!inode)
return;

/*
* The expectation is that the driver has successfully marked
* the resource busy by this point, so devmem_is_allowed()
* should start returning false, however for performance this
* does not iterate the entire resource range.
*/
if (devmem_is_allowed(PHYS_PFN(res->start)) &&
devmem_is_allowed(PHYS_PFN(res->end))) {
/*
* *cringe* iomem=relaxed says "go ahead, what's the
* worst that can happen?"
*/
return;
}

unmap_mapping_range(inode->i_mapping, res->start, resource_size(res), 1);
}
#endif

static int open_port(struct inode *inode, struct file *filp)
{
int rc;
Expand All @@ -884,8 +845,7 @@ static int open_port(struct inode *inode, struct file *filp)
* revocations when drivers want to take over a /dev/mem mapped
* range.
*/
inode->i_mapping = devmem_inode->i_mapping;
filp->f_mapping = inode->i_mapping;
filp->f_mapping = iomem_get_mapping();

return 0;
}
Expand Down Expand Up @@ -1017,48 +977,6 @@ static char *mem_devnode(struct device *dev, umode_t *mode)

static struct class *mem_class;

static int devmem_fs_init_fs_context(struct fs_context *fc)
{
return init_pseudo(fc, DEVMEM_MAGIC) ? 0 : -ENOMEM;
}

static struct file_system_type devmem_fs_type = {
.name = "devmem",
.owner = THIS_MODULE,
.init_fs_context = devmem_fs_init_fs_context,
.kill_sb = kill_anon_super,
};

static int devmem_init_inode(void)
{
static struct vfsmount *devmem_vfs_mount;
static int devmem_fs_cnt;
struct inode *inode;
int rc;

rc = simple_pin_fs(&devmem_fs_type, &devmem_vfs_mount, &devmem_fs_cnt);
if (rc < 0) {
pr_err("Cannot mount /dev/mem pseudo filesystem: %d\n", rc);
return rc;
}

inode = alloc_anon_inode(devmem_vfs_mount->mnt_sb);
if (IS_ERR(inode)) {
rc = PTR_ERR(inode);
pr_err("Cannot allocate inode for /dev/mem: %d\n", rc);
simple_release_fs(&devmem_vfs_mount, &devmem_fs_cnt);
return rc;
}

/*
* Publish /dev/mem initialized.
* Pairs with smp_load_acquire() in revoke_devmem().
*/
smp_store_release(&devmem_inode, inode);

return 0;
}

static int __init chr_dev_init(void)
{
int minor;
Expand All @@ -1080,8 +998,6 @@ static int __init chr_dev_init(void)
*/
if ((minor == DEVPORT_MINOR) && !arch_has_dev_port())
continue;
if ((minor == DEVMEM_MINOR) && devmem_init_inode() != 0)
continue;

device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
NULL, devlist[minor].name);
Expand Down
1 change: 0 additions & 1 deletion drivers/gpu/drm/exynos/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ comment "Sub-drivers"
config DRM_EXYNOS_G2D
bool "G2D"
depends on VIDEO_SAMSUNG_S5P_G2D=n || COMPILE_TEST
select FRAME_VECTOR
help
Choose this option if you want to use Exynos G2D for DRM.

Expand Down
48 changes: 21 additions & 27 deletions drivers/gpu/drm/exynos/exynos_drm_g2d.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ struct g2d_cmdlist_userptr {
dma_addr_t dma_addr;
unsigned long userptr;
unsigned long size;
struct frame_vector *vec;
struct page **pages;
unsigned int npages;
struct sg_table *sgt;
atomic_t refcount;
bool in_pool;
Expand Down Expand Up @@ -378,7 +379,6 @@ static void g2d_userptr_put_dma_addr(struct g2d_data *g2d,
bool force)
{
struct g2d_cmdlist_userptr *g2d_userptr = obj;
struct page **pages;

if (!obj)
return;
Expand All @@ -398,15 +398,9 @@ static void g2d_userptr_put_dma_addr(struct g2d_data *g2d,
dma_unmap_sgtable(to_dma_dev(g2d->drm_dev), g2d_userptr->sgt,
DMA_BIDIRECTIONAL, 0);

pages = frame_vector_pages(g2d_userptr->vec);
if (!IS_ERR(pages)) {
int i;

for (i = 0; i < frame_vector_count(g2d_userptr->vec); i++)
set_page_dirty_lock(pages[i]);
}
put_vaddr_frames(g2d_userptr->vec);
frame_vector_destroy(g2d_userptr->vec);
unpin_user_pages_dirty_lock(g2d_userptr->pages, g2d_userptr->npages,
true);
kvfree(g2d_userptr->pages);

if (!g2d_userptr->out_of_list)
list_del_init(&g2d_userptr->list);
Expand Down Expand Up @@ -474,35 +468,35 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct g2d_data *g2d,
offset = userptr & ~PAGE_MASK;
end = PAGE_ALIGN(userptr + size);
npages = (end - start) >> PAGE_SHIFT;
g2d_userptr->vec = frame_vector_create(npages);
if (!g2d_userptr->vec) {
g2d_userptr->pages = kvmalloc_array(npages, sizeof(*g2d_userptr->pages),
GFP_KERNEL);
if (!g2d_userptr->pages) {
ret = -ENOMEM;
goto err_free;
}

ret = get_vaddr_frames(start, npages, FOLL_FORCE | FOLL_WRITE,
g2d_userptr->vec);
ret = pin_user_pages_fast(start, npages,
FOLL_FORCE | FOLL_WRITE | FOLL_LONGTERM,
g2d_userptr->pages);
if (ret != npages) {
DRM_DEV_ERROR(g2d->dev,
"failed to get user pages from userptr.\n");
if (ret < 0)
goto err_destroy_framevec;
ret = -EFAULT;
goto err_put_framevec;
}
if (frame_vector_to_pages(g2d_userptr->vec) < 0) {
goto err_destroy_pages;
npages = ret;
ret = -EFAULT;
goto err_put_framevec;
goto err_unpin_pages;
}
g2d_userptr->npages = npages;

sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt) {
ret = -ENOMEM;
goto err_put_framevec;
goto err_unpin_pages;
}

ret = sg_alloc_table_from_pages(sgt,
frame_vector_pages(g2d_userptr->vec),
g2d_userptr->pages,
npages, offset, size, GFP_KERNEL);
if (ret < 0) {
DRM_DEV_ERROR(g2d->dev, "failed to get sgt from pages.\n");
Expand Down Expand Up @@ -538,11 +532,11 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct g2d_data *g2d,
err_free_sgt:
kfree(sgt);

err_put_framevec:
put_vaddr_frames(g2d_userptr->vec);
err_unpin_pages:
unpin_user_pages(g2d_userptr->pages, npages);

err_destroy_framevec:
frame_vector_destroy(g2d_userptr->vec);
err_destroy_pages:
kvfree(g2d_userptr->pages);

err_free:
kfree(g2d_userptr);
Expand Down
1 change: 0 additions & 1 deletion drivers/media/common/videobuf2/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ config VIDEOBUF2_V4L2

config VIDEOBUF2_MEMOPS
tristate
select FRAME_VECTOR

config VIDEOBUF2_DMA_CONTIG
tristate
Expand Down
1 change: 1 addition & 0 deletions drivers/media/common/videobuf2/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
videobuf2-common-objs := videobuf2-core.o
videobuf2-common-objs += frame_vector.o

ifeq ($(CONFIG_TRACEPOINTS),y)
videobuf2-common-objs += vb2-trace.o
Expand Down
55 changes: 19 additions & 36 deletions mm/frame_vector.c → ...ers/media/common/videobuf2/frame_vector.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <linux/pagemap.h>
#include <linux/sched.h>

#include <media/frame_vector.h>

/**
* get_vaddr_frames() - map virtual addresses to pfns
* @start: starting user address
Expand All @@ -32,13 +34,12 @@
* This function takes care of grabbing mmap_lock as necessary.
*/
int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
unsigned int gup_flags, struct frame_vector *vec)
struct frame_vector *vec)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
int ret = 0;
int err;
int locked;

if (nr_frames == 0)
return 0;
Expand All @@ -48,40 +49,26 @@ int get_vaddr_frames(unsigned long start, unsigned int nr_frames,

start = untagged_addr(start);

mmap_read_lock(mm);
locked = 1;
vma = find_vma_intersection(mm, start, start + 1);
if (!vma) {
ret = -EFAULT;
goto out;
}

/*
* While get_vaddr_frames() could be used for transient (kernel
* controlled lifetime) pinning of memory pages all current
* users establish long term (userspace controlled lifetime)
* page pinning. Treat get_vaddr_frames() like
* get_user_pages_longterm() and disallow it for filesystem-dax
* mappings.
*/
if (vma_is_fsdax(vma)) {
ret = -EOPNOTSUPP;
goto out;
}

if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
ret = pin_user_pages_fast(start, nr_frames,
FOLL_FORCE | FOLL_WRITE | FOLL_LONGTERM,
(struct page **)(vec->ptrs));
if (ret > 0) {
vec->got_ref = true;
vec->is_pfns = false;
ret = pin_user_pages_locked(start, nr_frames,
gup_flags, (struct page **)(vec->ptrs), &locked);
goto out;
goto out_unlocked;
}

mmap_read_lock(mm);
vec->got_ref = false;
vec->is_pfns = true;
ret = 0;
do {
unsigned long *nums = frame_vector_pfns(vec);

vma = find_vma_intersection(mm, start, start + 1);
if (!vma)
break;

while (ret < nr_frames && start + PAGE_SIZE <= vma->vm_end) {
err = follow_pfn(vma, start, &nums[ret]);
if (err) {
Expand All @@ -92,17 +79,13 @@ int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
start += PAGE_SIZE;
ret++;
}
/*
* We stop if we have enough pages or if VMA doesn't completely
* cover the tail page.
*/
if (ret >= nr_frames || start < vma->vm_end)
/* Bail out if VMA doesn't completely cover the tail page. */
if (start < vma->vm_end)
break;
vma = find_vma_intersection(mm, start, start + 1);
} while (vma && vma->vm_flags & (VM_IO | VM_PFNMAP));
} while (ret < nr_frames);
out:
if (locked)
mmap_read_unlock(mm);
mmap_read_unlock(mm);
out_unlocked:
if (!ret)
ret = -EFAULT;
if (ret > 0)
Expand Down
3 changes: 1 addition & 2 deletions drivers/media/common/videobuf2/videobuf2-memops.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,14 @@ struct frame_vector *vb2_create_framevec(unsigned long start,
unsigned long first, last;
unsigned long nr;
struct frame_vector *vec;
unsigned int flags = FOLL_FORCE | FOLL_WRITE;

first = start >> PAGE_SHIFT;
last = (start + length - 1) >> PAGE_SHIFT;
nr = last - first + 1;
vec = frame_vector_create(nr);
if (!vec)
return ERR_PTR(-ENOMEM);
ret = get_vaddr_frames(start & PAGE_MASK, nr, flags, vec);
ret = get_vaddr_frames(start & PAGE_MASK, nr, vec);
if (ret < 0)
goto out_destroy;
/* We accept only complete set of PFNs */
Expand Down
1 change: 0 additions & 1 deletion drivers/media/platform/omap/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ config VIDEO_OMAP2_VOUT
depends on VIDEO_V4L2
select VIDEOBUF2_DMA_CONTIG
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
select FRAME_VECTOR
help
V4L2 Display driver support for OMAP2/3 based boards.
1 change: 0 additions & 1 deletion drivers/misc/habanalabs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
config HABANA_AI
tristate "HabanaAI accelerators (habanalabs)"
depends on PCI && HAS_IOMEM
select FRAME_VECTOR
select GENERIC_ALLOCATOR
select HWMON
help
Expand Down
6 changes: 4 additions & 2 deletions drivers/misc/habanalabs/common/habanalabs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,8 @@ struct hl_ctx_mgr {
* struct hl_userptr - memory mapping chunk information
* @vm_type: type of the VM.
* @job_node: linked-list node for hanging the object on the Job's list.
* @vec: pointer to the frame vector.
* @pages: pointer to struct page array
* @npages: size of @pages array
* @sgt: pointer to the scatter-gather table that holds the pages.
* @dir: for DMA unmapping, the direction must be supplied, so save it.
* @debugfs_list: node in debugfs list of command submissions.
Expand All @@ -1100,7 +1101,8 @@ struct hl_ctx_mgr {
struct hl_userptr {
enum vm_type_t vm_type; /* must be first */
struct list_head job_node;
struct frame_vector *vec;
struct page **pages;
unsigned int npages;
struct sg_table *sgt;
enum dma_data_direction dir;
struct list_head debugfs_list;
Expand Down
Loading

0 comments on commit e913a8c

Please sign in to comment.