aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/mic/host/mic_fops.c
diff options
context:
space:
mode:
authorAshutosh Dixit <ashutosh.dixit@intel.com>2013-09-05 19:42:18 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-09-26 16:50:56 -0400
commitf69bcbf3b4c4b333dcd7a48eaf868bf0c88edab5 (patch)
tree82edf895c9db652788945faafb8d4301f79cb5f8 /drivers/misc/mic/host/mic_fops.c
parentaa27badd8972adb731f05d49ab74ec63e0826935 (diff)
Intel MIC Host Driver Changes for Virtio Devices.
This patch introduces the host "Virtio over PCIe" interface for Intel MIC. It allows creating user space backends on the host and instantiating virtio devices for them on the Intel MIC card. It uses the existing VRINGH infrastructure in the kernel to access virtio rings from the host. A character device per MIC is exposed with IOCTL, mmap and poll callbacks. This allows the user space backend to: (a) add/remove a virtio device via a device page. (b) map (R/O) virtio rings and device page to user space. (c) poll for availability of data. (d) copy a descriptor or entire descriptor chain to/from the card. (e) modify virtio configuration. (f) handle virtio device reset. The buffers are copied over using CPU copies for this initial patch and host initiated MIC DMA support is planned for future patches. The avail and desc virtio rings are in host memory and the used ring is in card memory to maximize writes across PCIe for performance. Co-author: Sudeep Dutt <sudeep.dutt@intel.com> Signed-off-by: Ashutosh Dixit <ashutosh.dixit@intel.com> Signed-off-by: Caz Yokoyama <Caz.Yokoyama@intel.com> Signed-off-by: Dasaratharaman Chandramouli <dasaratharaman.chandramouli@intel.com> Signed-off-by: Nikhil Rao <nikhil.rao@intel.com> Signed-off-by: Harshavardhan R Kharche <harshavardhan.r.kharche@intel.com> Signed-off-by: Sudeep Dutt <sudeep.dutt@intel.com> Acked-by: Yaozu (Eddie) Dong <eddie.dong@intel.com> Reviewed-by: Peter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/mic/host/mic_fops.c')
-rw-r--r--drivers/misc/mic/host/mic_fops.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c
new file mode 100644
index 000000000000..661469ad339d
--- /dev/null
+++ b/drivers/misc/mic/host/mic_fops.c
@@ -0,0 +1,221 @@
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
23#include <linux/mic_common.h>
24#include "../common/mic_device.h"
25#include "mic_device.h"
26#include "mic_fops.h"
27#include "mic_virtio.h"
28
29int mic_open(struct inode *inode, struct file *f)
30{
31 struct mic_vdev *mvdev;
32 struct mic_device *mdev = container_of(inode->i_cdev,
33 struct mic_device, cdev);
34
35 mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL);
36 if (!mvdev)
37 return -ENOMEM;
38
39 init_waitqueue_head(&mvdev->waitq);
40 INIT_LIST_HEAD(&mvdev->list);
41 mvdev->mdev = mdev;
42 mvdev->virtio_id = -1;
43
44 f->private_data = mvdev;
45 return 0;
46}
47
48int mic_release(struct inode *inode, struct file *f)
49{
50 struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
51
52 if (-1 != mvdev->virtio_id)
53 mic_virtio_del_device(mvdev);
54 f->private_data = NULL;
55 kfree(mvdev);
56 return 0;
57}
58
59long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
60{
61 struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
62 void __user *argp = (void __user *)arg;
63 int ret;
64
65 switch (cmd) {
66 case MIC_VIRTIO_ADD_DEVICE:
67 {
68 ret = mic_virtio_add_device(mvdev, argp);
69 if (ret < 0) {
70 dev_err(mic_dev(mvdev),
71 "%s %d errno ret %d\n",
72 __func__, __LINE__, ret);
73 return ret;
74 }
75 break;
76 }
77 case MIC_VIRTIO_COPY_DESC:
78 {
79 struct mic_copy_desc copy;
80
81 ret = mic_vdev_inited(mvdev);
82 if (ret)
83 return ret;
84
85 if (copy_from_user(&copy, argp, sizeof(copy)))
86 return -EFAULT;
87
88 dev_dbg(mic_dev(mvdev),
89 "%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n",
90 __func__, __LINE__, copy.iovcnt, copy.vr_idx,
91 copy.update_used);
92
93 ret = mic_virtio_copy_desc(mvdev, &copy);
94 if (ret < 0) {
95 dev_err(mic_dev(mvdev),
96 "%s %d errno ret %d\n",
97 __func__, __LINE__, ret);
98 return ret;
99 }
100 if (copy_to_user(
101 &((struct mic_copy_desc __user *)argp)->out_len,
102 &copy.out_len, sizeof(copy.out_len))) {
103 dev_err(mic_dev(mvdev), "%s %d errno ret %d\n",
104 __func__, __LINE__, -EFAULT);
105 return -EFAULT;
106 }
107 break;
108 }
109 case MIC_VIRTIO_CONFIG_CHANGE:
110 {
111 ret = mic_vdev_inited(mvdev);
112 if (ret)
113 return ret;
114
115 ret = mic_virtio_config_change(mvdev, argp);
116 if (ret < 0) {
117 dev_err(mic_dev(mvdev),
118 "%s %d errno ret %d\n",
119 __func__, __LINE__, ret);
120 return ret;
121 }
122 break;
123 }
124 default:
125 return -ENOIOCTLCMD;
126 };
127 return 0;
128}
129
130/*
131 * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and
132 * not when previously enqueued buffers may be available. This means that
133 * in the card->host (TX) path, when userspace is unblocked by poll it
134 * must drain all available descriptors or it can stall.
135 */
136unsigned int mic_poll(struct file *f, poll_table *wait)
137{
138 struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
139 int mask = 0;
140
141 poll_wait(f, &mvdev->waitq, wait);
142
143 if (mic_vdev_inited(mvdev))
144 mask = POLLERR;
145 else if (mvdev->poll_wake) {
146 mvdev->poll_wake = 0;
147 mask = POLLIN | POLLOUT;
148 }
149
150 return mask;
151}
152
153static inline int
154mic_query_offset(struct mic_vdev *mvdev, unsigned long offset,
155 unsigned long *size, unsigned long *pa)
156{
157 struct mic_device *mdev = mvdev->mdev;
158 unsigned long start = MIC_DP_SIZE;
159 int i;
160
161 /*
162 * MMAP interface is as follows:
163 * offset region
164 * 0x0 virtio device_page
165 * 0x1000 first vring
166 * 0x1000 + size of 1st vring second vring
167 * ....
168 */
169 if (!offset) {
170 *pa = virt_to_phys(mdev->dp);
171 *size = MIC_DP_SIZE;
172 return 0;
173 }
174
175 for (i = 0; i < mvdev->dd->num_vq; i++) {
176 struct mic_vringh *mvr = &mvdev->mvr[i];
177 if (offset == start) {
178 *pa = virt_to_phys(mvr->vring.va);
179 *size = mvr->vring.len;
180 return 0;
181 }
182 start += mvr->vring.len;
183 }
184 return -1;
185}
186
187/*
188 * Maps the device page and virtio rings to user space for readonly access.
189 */
190int
191mic_mmap(struct file *f, struct vm_area_struct *vma)
192{
193 struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
194 unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
195 unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size;
196 int i, err;
197
198 err = mic_vdev_inited(mvdev);
199 if (err)
200 return err;
201
202 if (vma->vm_flags & VM_WRITE)
203 return -EACCES;
204
205 while (size_rem) {
206 i = mic_query_offset(mvdev, offset, &size, &pa);
207 if (i < 0)
208 return -EINVAL;
209 err = remap_pfn_range(vma, vma->vm_start + offset,
210 pa >> PAGE_SHIFT, size, vma->vm_page_prot);
211 if (err)
212 return err;
213 dev_dbg(mic_dev(mvdev),
214 "%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n",
215 __func__, __LINE__, mvdev->virtio_id, size, offset,
216 pa, vma->vm_start + offset);
217 size_rem -= size;
218 offset += size;
219 }
220 return 0;
221}