root/drivers/xen/privcmd-buf.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. privcmd_buf_open
  2. privcmd_buf_vmapriv_free
  3. privcmd_buf_release
  4. privcmd_buf_vma_open
  5. privcmd_buf_vma_close
  6. privcmd_buf_vma_fault
  7. privcmd_buf_mmap

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

/* [<][>][^][v][top][bottom][index][help] */