brintos

brintos / linux-shallow public Read only

0
0
Text · 4.1 KiB · 0f0dad4 Raw
193 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR MIT2 3/******************************************************************************4 * privcmd-buf.c5 *6 * Mmap of hypercall buffers.7 *8 * Copyright (c) 2018 Juergen Gross9 */10 11#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt12 13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/list.h>16#include <linux/miscdevice.h>17#include <linux/mm.h>18#include <linux/slab.h>19 20#include "privcmd.h"21 22MODULE_DESCRIPTION("Xen Mmap of hypercall buffers");23MODULE_LICENSE("GPL");24 25struct privcmd_buf_private {26	struct mutex lock;27	struct list_head list;28};29 30struct privcmd_buf_vma_private {31	struct privcmd_buf_private *file_priv;32	struct list_head list;33	unsigned int users;34	unsigned int n_pages;35	struct page *pages[];36};37 38static int privcmd_buf_open(struct inode *ino, struct file *file)39{40	struct privcmd_buf_private *file_priv;41 42	file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);43	if (!file_priv)44		return -ENOMEM;45 46	mutex_init(&file_priv->lock);47	INIT_LIST_HEAD(&file_priv->list);48 49	file->private_data = file_priv;50 51	return 0;52}53 54static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv)55{56	unsigned int i;57 58	list_del(&vma_priv->list);59 60	for (i = 0; i < vma_priv->n_pages; i++)61		__free_page(vma_priv->pages[i]);62 63	kfree(vma_priv);64}65 66static int privcmd_buf_release(struct inode *ino, struct file *file)67{68	struct privcmd_buf_private *file_priv = file->private_data;69	struct privcmd_buf_vma_private *vma_priv;70 71	mutex_lock(&file_priv->lock);72 73	while (!list_empty(&file_priv->list)) {74		vma_priv = list_first_entry(&file_priv->list,75					    struct privcmd_buf_vma_private,76					    list);77		privcmd_buf_vmapriv_free(vma_priv);78	}79 80	mutex_unlock(&file_priv->lock);81 82	kfree(file_priv);83 84	return 0;85}86 87static void privcmd_buf_vma_open(struct vm_area_struct *vma)88{89	struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;90 91	if (!vma_priv)92		return;93 94	mutex_lock(&vma_priv->file_priv->lock);95	vma_priv->users++;96	mutex_unlock(&vma_priv->file_priv->lock);97}98 99static void privcmd_buf_vma_close(struct vm_area_struct *vma)100{101	struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;102	struct privcmd_buf_private *file_priv;103 104	if (!vma_priv)105		return;106 107	file_priv = vma_priv->file_priv;108 109	mutex_lock(&file_priv->lock);110 111	vma_priv->users--;112	if (!vma_priv->users)113		privcmd_buf_vmapriv_free(vma_priv);114 115	mutex_unlock(&file_priv->lock);116}117 118static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf)119{120	pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",121		 vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end,122		 vmf->pgoff, (void *)vmf->address);123 124	return VM_FAULT_SIGBUS;125}126 127static const struct vm_operations_struct privcmd_buf_vm_ops = {128	.open = privcmd_buf_vma_open,129	.close = privcmd_buf_vma_close,130	.fault = privcmd_buf_vma_fault,131};132 133static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma)134{135	struct privcmd_buf_private *file_priv = file->private_data;136	struct privcmd_buf_vma_private *vma_priv;137	unsigned long count = vma_pages(vma);138	unsigned int i;139	int ret = 0;140 141	if (!(vma->vm_flags & VM_SHARED))142		return -EINVAL;143 144	vma_priv = kzalloc(struct_size(vma_priv, pages, count), GFP_KERNEL);145	if (!vma_priv)146		return -ENOMEM;147 148	for (i = 0; i < count; i++) {149		vma_priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);150		if (!vma_priv->pages[i])151			break;152		vma_priv->n_pages++;153	}154 155	mutex_lock(&file_priv->lock);156 157	vma_priv->file_priv = file_priv;158	vma_priv->users = 1;159 160	vm_flags_set(vma, VM_IO | VM_DONTEXPAND);161	vma->vm_ops = &privcmd_buf_vm_ops;162	vma->vm_private_data = vma_priv;163 164	list_add(&vma_priv->list, &file_priv->list);165 166	if (vma_priv->n_pages != count)167		ret = -ENOMEM;168	else169		ret = vm_map_pages_zero(vma, vma_priv->pages,170						vma_priv->n_pages);171 172	if (ret)173		privcmd_buf_vmapriv_free(vma_priv);174 175	mutex_unlock(&file_priv->lock);176 177	return ret;178}179 180const struct file_operations xen_privcmdbuf_fops = {181	.owner = THIS_MODULE,182	.open = privcmd_buf_open,183	.release = privcmd_buf_release,184	.mmap = privcmd_buf_mmap,185};186EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);187 188struct miscdevice xen_privcmdbuf_dev = {189	.minor = MISC_DYNAMIC_MINOR,190	.name = "xen/hypercall",191	.fops = &xen_privcmdbuf_fops,192};193