mirror of
				https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
				synced 2025-11-05 01:03:37 +10:00 
			
		
		
		
	Replace direct modifications to vma->vm_flags with calls to modifier functions to be able to track flag changes and to keep vma locking correctness. [akpm@linux-foundation.org: fix drivers/misc/open-dice.c, per Hyeonggon Yoo] Link: https://lkml.kernel.org/r/20230126193752.297968-5-surenb@google.com Signed-off-by: Suren Baghdasaryan <surenb@google.com> Acked-by: Michal Hocko <mhocko@suse.com> Acked-by: Mel Gorman <mgorman@techsingularity.net> Acked-by: Mike Rapoport (IBM) <rppt@kernel.org> Acked-by: Sebastian Reichel <sebastian.reichel@collabora.com> Reviewed-by: Liam R. Howlett <Liam.Howlett@Oracle.com> Reviewed-by: Hyeonggon Yoo <42.hyeyoo@gmail.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Arjun Roy <arjunroy@google.com> Cc: Axel Rasmussen <axelrasmussen@google.com> Cc: David Hildenbrand <david@redhat.com> Cc: David Howells <dhowells@redhat.com> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: David Rientjes <rientjes@google.com> Cc: Eric Dumazet <edumazet@google.com> Cc: Greg Thelen <gthelen@google.com> Cc: Hugh Dickins <hughd@google.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Jann Horn <jannh@google.com> Cc: Joel Fernandes <joelaf@google.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Kent Overstreet <kent.overstreet@linux.dev> Cc: Laurent Dufour <ldufour@linux.ibm.com> Cc: Lorenzo Stoakes <lstoakes@gmail.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: Minchan Kim <minchan@google.com> Cc: Paul E. McKenney <paulmck@kernel.org> Cc: Peter Oskolkov <posk@google.com> Cc: Peter Xu <peterx@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Punit Agrawal <punit.agrawal@bytedance.com> Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Cc: Shakeel Butt <shakeelb@google.com> Cc: Soheil Hassas Yeganeh <soheil@google.com> Cc: Song Liu <songliubraving@fb.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Will Deacon <will@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
		
			
				
	
	
		
			192 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0 OR MIT
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
 * privcmd-buf.c
 | 
						|
 *
 | 
						|
 * Mmap of hypercall buffers.
 | 
						|
 *
 | 
						|
 * Copyright (c) 2018 Juergen Gross
 | 
						|
 */
 | 
						|
 | 
						|
#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
 | 
						|
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/list.h>
 | 
						|
#include <linux/miscdevice.h>
 | 
						|
#include <linux/mm.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
 | 
						|
#include "privcmd.h"
 | 
						|
 | 
						|
MODULE_LICENSE("GPL");
 | 
						|
 | 
						|
struct privcmd_buf_private {
 | 
						|
	struct mutex lock;
 | 
						|
	struct list_head list;
 | 
						|
};
 | 
						|
 | 
						|
struct privcmd_buf_vma_private {
 | 
						|
	struct privcmd_buf_private *file_priv;
 | 
						|
	struct list_head list;
 | 
						|
	unsigned int users;
 | 
						|
	unsigned int n_pages;
 | 
						|
	struct page *pages[];
 | 
						|
};
 | 
						|
 | 
						|
static int privcmd_buf_open(struct inode *ino, struct file *file)
 | 
						|
{
 | 
						|
	struct privcmd_buf_private *file_priv;
 | 
						|
 | 
						|
	file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
 | 
						|
	if (!file_priv)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	mutex_init(&file_priv->lock);
 | 
						|
	INIT_LIST_HEAD(&file_priv->list);
 | 
						|
 | 
						|
	file->private_data = file_priv;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv)
 | 
						|
{
 | 
						|
	unsigned int i;
 | 
						|
 | 
						|
	list_del(&vma_priv->list);
 | 
						|
 | 
						|
	for (i = 0; i < vma_priv->n_pages; i++)
 | 
						|
		__free_page(vma_priv->pages[i]);
 | 
						|
 | 
						|
	kfree(vma_priv);
 | 
						|
}
 | 
						|
 | 
						|
static int privcmd_buf_release(struct inode *ino, struct file *file)
 | 
						|
{
 | 
						|
	struct privcmd_buf_private *file_priv = file->private_data;
 | 
						|
	struct privcmd_buf_vma_private *vma_priv;
 | 
						|
 | 
						|
	mutex_lock(&file_priv->lock);
 | 
						|
 | 
						|
	while (!list_empty(&file_priv->list)) {
 | 
						|
		vma_priv = list_first_entry(&file_priv->list,
 | 
						|
					    struct privcmd_buf_vma_private,
 | 
						|
					    list);
 | 
						|
		privcmd_buf_vmapriv_free(vma_priv);
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_unlock(&file_priv->lock);
 | 
						|
 | 
						|
	kfree(file_priv);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void privcmd_buf_vma_open(struct vm_area_struct *vma)
 | 
						|
{
 | 
						|
	struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
 | 
						|
 | 
						|
	if (!vma_priv)
 | 
						|
		return;
 | 
						|
 | 
						|
	mutex_lock(&vma_priv->file_priv->lock);
 | 
						|
	vma_priv->users++;
 | 
						|
	mutex_unlock(&vma_priv->file_priv->lock);
 | 
						|
}
 | 
						|
 | 
						|
static void privcmd_buf_vma_close(struct vm_area_struct *vma)
 | 
						|
{
 | 
						|
	struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
 | 
						|
	struct privcmd_buf_private *file_priv;
 | 
						|
 | 
						|
	if (!vma_priv)
 | 
						|
		return;
 | 
						|
 | 
						|
	file_priv = vma_priv->file_priv;
 | 
						|
 | 
						|
	mutex_lock(&file_priv->lock);
 | 
						|
 | 
						|
	vma_priv->users--;
 | 
						|
	if (!vma_priv->users)
 | 
						|
		privcmd_buf_vmapriv_free(vma_priv);
 | 
						|
 | 
						|
	mutex_unlock(&file_priv->lock);
 | 
						|
}
 | 
						|
 | 
						|
static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf)
 | 
						|
{
 | 
						|
	pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
 | 
						|
		 vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end,
 | 
						|
		 vmf->pgoff, (void *)vmf->address);
 | 
						|
 | 
						|
	return VM_FAULT_SIGBUS;
 | 
						|
}
 | 
						|
 | 
						|
static const struct vm_operations_struct privcmd_buf_vm_ops = {
 | 
						|
	.open = privcmd_buf_vma_open,
 | 
						|
	.close = privcmd_buf_vma_close,
 | 
						|
	.fault = privcmd_buf_vma_fault,
 | 
						|
};
 | 
						|
 | 
						|
static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma)
 | 
						|
{
 | 
						|
	struct privcmd_buf_private *file_priv = file->private_data;
 | 
						|
	struct privcmd_buf_vma_private *vma_priv;
 | 
						|
	unsigned long count = vma_pages(vma);
 | 
						|
	unsigned int i;
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	if (!(vma->vm_flags & VM_SHARED))
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	vma_priv = kzalloc(struct_size(vma_priv, pages, count), GFP_KERNEL);
 | 
						|
	if (!vma_priv)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	for (i = 0; i < count; i++) {
 | 
						|
		vma_priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
 | 
						|
		if (!vma_priv->pages[i])
 | 
						|
			break;
 | 
						|
		vma_priv->n_pages++;
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_lock(&file_priv->lock);
 | 
						|
 | 
						|
	vma_priv->file_priv = file_priv;
 | 
						|
	vma_priv->users = 1;
 | 
						|
 | 
						|
	vm_flags_set(vma, VM_IO | VM_DONTEXPAND);
 | 
						|
	vma->vm_ops = &privcmd_buf_vm_ops;
 | 
						|
	vma->vm_private_data = vma_priv;
 | 
						|
 | 
						|
	list_add(&vma_priv->list, &file_priv->list);
 | 
						|
 | 
						|
	if (vma_priv->n_pages != count)
 | 
						|
		ret = -ENOMEM;
 | 
						|
	else
 | 
						|
		ret = vm_map_pages_zero(vma, vma_priv->pages,
 | 
						|
						vma_priv->n_pages);
 | 
						|
 | 
						|
	if (ret)
 | 
						|
		privcmd_buf_vmapriv_free(vma_priv);
 | 
						|
 | 
						|
	mutex_unlock(&file_priv->lock);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
const struct file_operations xen_privcmdbuf_fops = {
 | 
						|
	.owner = THIS_MODULE,
 | 
						|
	.open = privcmd_buf_open,
 | 
						|
	.release = privcmd_buf_release,
 | 
						|
	.mmap = privcmd_buf_mmap,
 | 
						|
};
 | 
						|
EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
 | 
						|
 | 
						|
struct miscdevice xen_privcmdbuf_dev = {
 | 
						|
	.minor = MISC_DYNAMIC_MINOR,
 | 
						|
	.name = "xen/hypercall",
 | 
						|
	.fops = &xen_privcmdbuf_fops,
 | 
						|
};
 |