1 /*
2  * Intel MIC Platform Software Stack (MPSS)
3  *
4  * Copyright(c) 2013 Intel Corporation.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version 2, as
8  * published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * General Public License for more details.
14  *
15  * The full GNU General Public License is included in this distribution in
16  * the file called "COPYING".
17  *
18  * Intel MIC Host driver.
19  *
20  */
21 #include <linux/poll.h>
22 #include <linux/pci.h>
23 
24 #include <linux/mic_common.h>
25 #include "../common/mic_dev.h"
26 #include "mic_device.h"
27 #include "mic_fops.h"
28 #include "mic_virtio.h"
29 
mic_open(struct inode * inode,struct file * f)30 int mic_open(struct inode *inode, struct file *f)
31 {
32 	struct mic_vdev *mvdev;
33 	struct mic_device *mdev = container_of(f->private_data,
34 		struct mic_device, miscdev);
35 
36 	mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL);
37 	if (!mvdev)
38 		return -ENOMEM;
39 
40 	init_waitqueue_head(&mvdev->waitq);
41 	INIT_LIST_HEAD(&mvdev->list);
42 	mvdev->mdev = mdev;
43 	mvdev->virtio_id = -1;
44 
45 	f->private_data = mvdev;
46 	return 0;
47 }
48 
mic_release(struct inode * inode,struct file * f)49 int mic_release(struct inode *inode, struct file *f)
50 {
51 	struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
52 
53 	if (-1 != mvdev->virtio_id)
54 		mic_virtio_del_device(mvdev);
55 	f->private_data = NULL;
56 	kfree(mvdev);
57 	return 0;
58 }
59 
mic_ioctl(struct file * f,unsigned int cmd,unsigned long arg)60 long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
61 {
62 	struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
63 	void __user *argp = (void __user *)arg;
64 	int ret;
65 
66 	switch (cmd) {
67 	case MIC_VIRTIO_ADD_DEVICE:
68 	{
69 		ret = mic_virtio_add_device(mvdev, argp);
70 		if (ret < 0) {
71 			dev_err(mic_dev(mvdev),
72 				"%s %d errno ret %d\n",
73 				__func__, __LINE__, ret);
74 			return ret;
75 		}
76 		break;
77 	}
78 	case MIC_VIRTIO_COPY_DESC:
79 	{
80 		struct mic_copy_desc copy;
81 
82 		ret = mic_vdev_inited(mvdev);
83 		if (ret)
84 			return ret;
85 
86 		if (copy_from_user(&copy, argp, sizeof(copy)))
87 			return -EFAULT;
88 
89 		dev_dbg(mic_dev(mvdev),
90 			"%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n",
91 			__func__, __LINE__, copy.iovcnt, copy.vr_idx,
92 			copy.update_used);
93 
94 		ret = mic_virtio_copy_desc(mvdev, &copy);
95 		if (ret < 0) {
96 			dev_err(mic_dev(mvdev),
97 				"%s %d errno ret %d\n",
98 				__func__, __LINE__, ret);
99 			return ret;
100 		}
101 		if (copy_to_user(
102 			&((struct mic_copy_desc __user *)argp)->out_len,
103 			&copy.out_len, sizeof(copy.out_len))) {
104 			dev_err(mic_dev(mvdev), "%s %d errno ret %d\n",
105 				__func__, __LINE__, -EFAULT);
106 			return -EFAULT;
107 		}
108 		break;
109 	}
110 	case MIC_VIRTIO_CONFIG_CHANGE:
111 	{
112 		ret = mic_vdev_inited(mvdev);
113 		if (ret)
114 			return ret;
115 
116 		ret = mic_virtio_config_change(mvdev, argp);
117 		if (ret < 0) {
118 			dev_err(mic_dev(mvdev),
119 				"%s %d errno ret %d\n",
120 				__func__, __LINE__, ret);
121 			return ret;
122 		}
123 		break;
124 	}
125 	default:
126 		return -ENOIOCTLCMD;
127 	};
128 	return 0;
129 }
130 
131 /*
132  * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and
133  * not when previously enqueued buffers may be available. This means that
134  * in the card->host (TX) path, when userspace is unblocked by poll it
135  * must drain all available descriptors or it can stall.
136  */
mic_poll(struct file * f,poll_table * wait)137 unsigned int mic_poll(struct file *f, poll_table *wait)
138 {
139 	struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
140 	int mask = 0;
141 
142 	poll_wait(f, &mvdev->waitq, wait);
143 
144 	if (mic_vdev_inited(mvdev)) {
145 		mask = POLLERR;
146 	} else if (mvdev->poll_wake) {
147 		mvdev->poll_wake = 0;
148 		mask = POLLIN | POLLOUT;
149 	}
150 
151 	return mask;
152 }
153 
154 static inline int
mic_query_offset(struct mic_vdev * mvdev,unsigned long offset,unsigned long * size,unsigned long * pa)155 mic_query_offset(struct mic_vdev *mvdev, unsigned long offset,
156 		 unsigned long *size, unsigned long *pa)
157 {
158 	struct mic_device *mdev = mvdev->mdev;
159 	unsigned long start = MIC_DP_SIZE;
160 	int i;
161 
162 	/*
163 	 * MMAP interface is as follows:
164 	 * offset				region
165 	 * 0x0					virtio device_page
166 	 * 0x1000				first vring
167 	 * 0x1000 + size of 1st vring		second vring
168 	 * ....
169 	 */
170 	if (!offset) {
171 		*pa = virt_to_phys(mdev->dp);
172 		*size = MIC_DP_SIZE;
173 		return 0;
174 	}
175 
176 	for (i = 0; i < mvdev->dd->num_vq; i++) {
177 		struct mic_vringh *mvr = &mvdev->mvr[i];
178 		if (offset == start) {
179 			*pa = virt_to_phys(mvr->vring.va);
180 			*size = mvr->vring.len;
181 			return 0;
182 		}
183 		start += mvr->vring.len;
184 	}
185 	return -1;
186 }
187 
188 /*
189  * Maps the device page and virtio rings to user space for readonly access.
190  */
191 int
mic_mmap(struct file * f,struct vm_area_struct * vma)192 mic_mmap(struct file *f, struct vm_area_struct *vma)
193 {
194 	struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
195 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
196 	unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size;
197 	int i, err;
198 
199 	err = mic_vdev_inited(mvdev);
200 	if (err)
201 		return err;
202 
203 	if (vma->vm_flags & VM_WRITE)
204 		return -EACCES;
205 
206 	while (size_rem) {
207 		i = mic_query_offset(mvdev, offset, &size, &pa);
208 		if (i < 0)
209 			return -EINVAL;
210 		err = remap_pfn_range(vma, vma->vm_start + offset,
211 			pa >> PAGE_SHIFT, size, vma->vm_page_prot);
212 		if (err)
213 			return err;
214 		dev_dbg(mic_dev(mvdev),
215 			"%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n",
216 			__func__, __LINE__, mvdev->virtio_id, size, offset,
217 			pa, vma->vm_start + offset);
218 		size_rem -= size;
219 		offset += size;
220 	}
221 	return 0;
222 }
223