at v6.19 192 lines 4.2 kB view raw
1// SPDX-License-Identifier: GPL-2.0 OR MIT 2 3/****************************************************************************** 4 * privcmd-buf.c 5 * 6 * Mmap of hypercall buffers. 7 * 8 * Copyright (c) 2018 Juergen Gross 9 */ 10 11#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt 12 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 else 169 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};