Linux Audio

Check our new training course

Loading...
v6.8
  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_LICENSE("GPL");
 23
 24struct privcmd_buf_private {
 25	struct mutex lock;
 26	struct list_head list;
 27};
 28
 29struct 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
 37static 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
 53static 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
 65static 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
 86static 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
 98static 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
117static 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
126static 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
132static 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	vm_flags_set(vma, 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
179const 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};
185EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
186
187struct miscdevice xen_privcmdbuf_dev = {
188	.minor = MISC_DYNAMIC_MINOR,
189	.name = "xen/hypercall",
190	.fops = &xen_privcmdbuf_fops,
191};
v5.9
  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_LICENSE("GPL");
 23
 24struct privcmd_buf_private {
 25	struct mutex lock;
 26	struct list_head list;
 27};
 28
 29struct 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
 37static 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
 53static 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
 65static 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
 86static 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
 98static 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
117static 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
126static 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
132static 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
179const 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};
185EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
186
187struct miscdevice xen_privcmdbuf_dev = {
188	.minor = MISC_DYNAMIC_MINOR,
189	.name = "xen/hypercall",
190	.fops = &xen_privcmdbuf_fops,
191};