blob: 8efc22db1794fd0e6eb6115bb37ad8018bbe9bc2 [file] [log] [blame]
Kristofer Jonsson116a6352020-08-20 17:25:23 +02001/*
2 * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
3 *
4 * This program is free software and is provided to you under the terms of the
5 * GNU General Public License version 2 as published by the Free Software
6 * Foundation, and any use by you of this program is subject to the terms
7 * of such GNU licence.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, you can access it online at
16 * http://www.gnu.org/licenses/gpl-2.0.html.
17 *
18 * SPDX-License-Identifier: GPL-2.0-only
19 */
20
21/****************************************************************************
22 * Includes
23 ****************************************************************************/
24
25#include "ethosu_inference.h"
26
27#include "ethosu_buffer.h"
28#include "ethosu_core_interface.h"
29#include "ethosu_device.h"
30#include "ethosu_network.h"
31#include "uapi/ethosu.h"
32
33#include <linux/anon_inodes.h>
34#include <linux/file.h>
35#include <linux/fs.h>
36#include <linux/poll.h>
37
38/****************************************************************************
39 * Variables
40 ****************************************************************************/
41
42static int ethosu_inference_release(struct inode *inode,
43 struct file *file);
44
45static unsigned int ethosu_inference_poll(struct file *file,
46 poll_table *wait);
47
48static long ethosu_inference_ioctl(struct file *file,
49 unsigned int cmd,
50 unsigned long arg);
51
52static const struct file_operations ethosu_inference_fops = {
53 .release = &ethosu_inference_release,
54 .poll = &ethosu_inference_poll,
55 .unlocked_ioctl = &ethosu_inference_ioctl,
56#ifdef CONFIG_COMPAT
57 .compat_ioctl = &ethosu_inference_ioctl,
58#endif
59};
60
61/****************************************************************************
62 * Functions
63 ****************************************************************************/
64
65static const char *status_to_string(const enum ethosu_uapi_status status)
66{
67 switch (status) {
68 case ETHOSU_UAPI_STATUS_OK: {
69 return "Ok";
70 }
71 case ETHOSU_UAPI_STATUS_ERROR: {
72 return "Error";
73 }
74 default: {
75 return "Unknown";
76 }
77 }
78}
79
80static int ethosu_inference_send(struct ethosu_inference *inf)
81{
82 int ret;
83
84 if (inf->pending)
85 return -EINVAL;
86
87 inf->status = ETHOSU_UAPI_STATUS_ERROR;
88
89 ret = ethosu_mailbox_inference(&inf->edev->mailbox, inf, inf->ifm,
90 inf->ofm, inf->net->buf);
91 if (ret)
92 return ret;
93
94 inf->pending = true;
95
96 ethosu_inference_get(inf);
97
98 return 0;
99}
100
101static int ethosu_inference_find(struct ethosu_inference *inf,
102 struct list_head *inference_list)
103{
104 struct ethosu_inference *cur;
105
106 list_for_each_entry(cur, inference_list, list) {
107 if (cur == inf)
108 return 0;
109 }
110
111 return -EINVAL;
112}
113
114static bool ethosu_inference_verify(struct file *file)
115{
116 return file->f_op == &ethosu_inference_fops;
117}
118
119static void ethosu_inference_kref_destroy(struct kref *kref)
120{
121 struct ethosu_inference *inf =
122 container_of(kref, struct ethosu_inference, kref);
123
124 dev_info(inf->edev->dev,
125 "Inference destroy. handle=0x%pK, status=%d\n",
126 inf, inf->status);
127
128 list_del(&inf->list);
129 ethosu_buffer_put(inf->ifm);
130 ethosu_buffer_put(inf->ofm);
131 ethosu_network_put(inf->net);
132 devm_kfree(inf->edev->dev, inf);
133}
134
135static int ethosu_inference_release(struct inode *inode,
136 struct file *file)
137{
138 struct ethosu_inference *inf = file->private_data;
139
140 dev_info(inf->edev->dev,
141 "Inference release. handle=0x%pK, status=%d\n",
142 inf, inf->status);
143
144 ethosu_inference_put(inf);
145
146 return 0;
147}
148
149static unsigned int ethosu_inference_poll(struct file *file,
150 poll_table *wait)
151{
152 struct ethosu_inference *inf = file->private_data;
153 int ret = 0;
154
155 poll_wait(file, &inf->waitq, wait);
156
157 if (!inf->pending)
158 ret |= POLLIN;
159
160 return ret;
161}
162
163static long ethosu_inference_ioctl(struct file *file,
164 unsigned int cmd,
165 unsigned long arg)
166{
167 struct ethosu_inference *inf = file->private_data;
168 int ret = -EINVAL;
169
170 ret = mutex_lock_interruptible(&inf->edev->mutex);
171 if (ret)
172 return ret;
173
174 dev_info(inf->edev->dev, "Ioctl: cmd=%u, arg=%lu\n", cmd, arg);
175
176 switch (cmd) {
177 case ETHOSU_IOCTL_INFERENCE_STATUS: {
178 ret = inf->status;
179
180 dev_info(inf->edev->dev,
181 "Ioctl: Inference status. status=%s (%d)\n",
182 status_to_string(ret), ret);
183 break;
184 }
185 default: {
186 dev_err(inf->edev->dev, "Invalid ioctl. cmd=%u, arg=%lu",
187 cmd, arg);
188 break;
189 }
190 }
191
192 mutex_unlock(&inf->edev->mutex);
193
194 return ret;
195}
196
197int ethosu_inference_create(struct ethosu_device *edev,
198 struct ethosu_network *net,
199 struct ethosu_uapi_inference_create *uapi)
200{
201 struct ethosu_inference *inf;
202 int fd;
203 int ret = -ENOMEM;
204
205 inf = devm_kzalloc(edev->dev, sizeof(*inf), GFP_KERNEL);
206 if (!inf)
207 return -ENOMEM;
208
209 inf->edev = edev;
210 inf->net = net;
211 inf->pending = false;
212 inf->status = ETHOSU_UAPI_STATUS_ERROR;
213 kref_init(&inf->kref);
214 init_waitqueue_head(&inf->waitq);
215
216 /* Get pointer to IFM buffer */
217 inf->ifm = ethosu_buffer_get_from_fd(uapi->ifm_fd);
218 if (IS_ERR(inf->ifm)) {
219 ret = PTR_ERR(inf->ifm);
220 goto free_inf;
221 }
222
223 /* Get pointer to OFM buffer */
224 inf->ofm = ethosu_buffer_get_from_fd(uapi->ofm_fd);
225 if (IS_ERR(inf->ofm)) {
226 ret = PTR_ERR(inf->ofm);
227 goto put_ifm;
228 }
229
230 /* Increment network reference count */
231 ethosu_network_get(net);
232
233 /* Create file descriptor */
234 ret = fd = anon_inode_getfd("ethosu-inference", &ethosu_inference_fops,
235 inf, O_RDWR | O_CLOEXEC);
236 if (ret < 0)
237 goto put_net;
238
239 /* Store pointer to file structure */
240 inf->file = fget(ret);
241 fput(inf->file);
242
243 /* Add inference to inference list */
244 list_add(&inf->list, &edev->inference_list);
245
246 /* Send inference request to Arm Ethos-U subsystem */
247 (void)ethosu_inference_send(inf);
248
249 dev_info(edev->dev, "Inference create. handle=0x%pK, fd=%d",
250 inf, fd);
251
252 return fd;
253
254put_net:
255 ethosu_network_put(inf->net);
256 ethosu_buffer_put(inf->ofm);
257
258put_ifm:
259 ethosu_buffer_put(inf->ifm);
260
261free_inf:
262 devm_kfree(edev->dev, inf);
263
264 return ret;
265}
266
267struct ethosu_inference *ethosu_inference_get_from_fd(int fd)
268{
269 struct ethosu_inference *inf;
270 struct file *file;
271
272 file = fget(fd);
273 if (!file)
274 return ERR_PTR(-EINVAL);
275
276 if (!ethosu_inference_verify(file)) {
277 fput(file);
278
279 return ERR_PTR(-EINVAL);
280 }
281
282 inf = file->private_data;
283 ethosu_inference_get(inf);
284 fput(file);
285
286 return inf;
287}
288
289void ethosu_inference_get(struct ethosu_inference *inf)
290{
291 kref_get(&inf->kref);
292}
293
294void ethosu_inference_put(struct ethosu_inference *inf)
295{
296 kref_put(&inf->kref, &ethosu_inference_kref_destroy);
297}
298
299void ethosu_inference_rsp(struct ethosu_device *edev,
300 struct ethosu_core_inference_rsp *rsp)
301{
302 struct ethosu_inference *inf =
303 (struct ethosu_inference *)rsp->user_arg;
304 int ret;
305
306 ret = ethosu_inference_find(inf, &edev->inference_list);
307 if (ret) {
308 dev_warn(edev->dev,
309 "Handle not found in inference list. handle=0x%p\n",
310 rsp);
311
312 return;
313 }
314
315 inf->pending = false;
316
317 if (rsp->status == ETHOSU_CORE_STATUS_OK) {
318 inf->status = ETHOSU_UAPI_STATUS_OK;
319
320 ret = ethosu_buffer_resize(inf->ofm,
321 inf->ofm->size + rsp->ofm_size,
322 inf->ofm->offset);
323 if (ret)
324 inf->status = ETHOSU_UAPI_STATUS_ERROR;
325 } else {
326 inf->status = ETHOSU_UAPI_STATUS_ERROR;
327 }
328
329 wake_up_interruptible(&inf->waitq);
330
331 ethosu_inference_put(inf);
332}