blob: e9530cf5c5cf07406ef186ec3a7f4a32601d9178 [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
Kristofer Jonssonb74492c2020-09-10 13:26:01 +020089 ret = ethosu_mailbox_inference(&inf->edev->mailbox, inf,
90 inf->ifm_count, inf->ifm,
91 inf->ofm_count, inf->ofm,
92 inf->net->buf);
Kristofer Jonsson116a6352020-08-20 17:25:23 +020093 if (ret)
94 return ret;
95
96 inf->pending = true;
97
98 ethosu_inference_get(inf);
99
100 return 0;
101}
102
103static int ethosu_inference_find(struct ethosu_inference *inf,
104 struct list_head *inference_list)
105{
106 struct ethosu_inference *cur;
107
108 list_for_each_entry(cur, inference_list, list) {
109 if (cur == inf)
110 return 0;
111 }
112
113 return -EINVAL;
114}
115
116static bool ethosu_inference_verify(struct file *file)
117{
118 return file->f_op == &ethosu_inference_fops;
119}
120
121static void ethosu_inference_kref_destroy(struct kref *kref)
122{
123 struct ethosu_inference *inf =
124 container_of(kref, struct ethosu_inference, kref);
125
126 dev_info(inf->edev->dev,
127 "Inference destroy. handle=0x%pK, status=%d\n",
128 inf, inf->status);
129
130 list_del(&inf->list);
Kristofer Jonssonb74492c2020-09-10 13:26:01 +0200131
132 while (inf->ifm_count-- > 0)
133 ethosu_buffer_put(inf->ifm[inf->ifm_count]);
134
135 while (inf->ofm_count-- > 0)
136 ethosu_buffer_put(inf->ofm[inf->ofm_count]);
137
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200138 ethosu_network_put(inf->net);
139 devm_kfree(inf->edev->dev, inf);
140}
141
142static int ethosu_inference_release(struct inode *inode,
143 struct file *file)
144{
145 struct ethosu_inference *inf = file->private_data;
146
147 dev_info(inf->edev->dev,
148 "Inference release. handle=0x%pK, status=%d\n",
149 inf, inf->status);
150
151 ethosu_inference_put(inf);
152
153 return 0;
154}
155
156static unsigned int ethosu_inference_poll(struct file *file,
157 poll_table *wait)
158{
159 struct ethosu_inference *inf = file->private_data;
160 int ret = 0;
161
162 poll_wait(file, &inf->waitq, wait);
163
164 if (!inf->pending)
165 ret |= POLLIN;
166
167 return ret;
168}
169
170static long ethosu_inference_ioctl(struct file *file,
171 unsigned int cmd,
172 unsigned long arg)
173{
174 struct ethosu_inference *inf = file->private_data;
175 int ret = -EINVAL;
176
177 ret = mutex_lock_interruptible(&inf->edev->mutex);
178 if (ret)
179 return ret;
180
181 dev_info(inf->edev->dev, "Ioctl: cmd=%u, arg=%lu\n", cmd, arg);
182
183 switch (cmd) {
184 case ETHOSU_IOCTL_INFERENCE_STATUS: {
185 ret = inf->status;
186
187 dev_info(inf->edev->dev,
188 "Ioctl: Inference status. status=%s (%d)\n",
189 status_to_string(ret), ret);
190 break;
191 }
192 default: {
193 dev_err(inf->edev->dev, "Invalid ioctl. cmd=%u, arg=%lu",
194 cmd, arg);
195 break;
196 }
197 }
198
199 mutex_unlock(&inf->edev->mutex);
200
201 return ret;
202}
203
204int ethosu_inference_create(struct ethosu_device *edev,
205 struct ethosu_network *net,
206 struct ethosu_uapi_inference_create *uapi)
207{
208 struct ethosu_inference *inf;
Kristofer Jonssonb74492c2020-09-10 13:26:01 +0200209 uint32_t i;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200210 int fd;
211 int ret = -ENOMEM;
212
213 inf = devm_kzalloc(edev->dev, sizeof(*inf), GFP_KERNEL);
214 if (!inf)
215 return -ENOMEM;
216
217 inf->edev = edev;
218 inf->net = net;
219 inf->pending = false;
220 inf->status = ETHOSU_UAPI_STATUS_ERROR;
221 kref_init(&inf->kref);
222 init_waitqueue_head(&inf->waitq);
223
Kristofer Jonssonb74492c2020-09-10 13:26:01 +0200224 /* Get pointer to IFM buffers */
225 for (i = 0; i < uapi->ifm_count; i++) {
226 inf->ifm[i] = ethosu_buffer_get_from_fd(uapi->ifm_fd[i]);
227 if (IS_ERR(inf->ifm[i])) {
228 ret = PTR_ERR(inf->ifm[i]);
229 goto put_ifm;
230 }
231
232 inf->ifm_count++;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200233 }
234
235 /* Get pointer to OFM buffer */
Kristofer Jonssonb74492c2020-09-10 13:26:01 +0200236 for (i = 0; i < uapi->ofm_count; i++) {
237 inf->ofm[i] = ethosu_buffer_get_from_fd(uapi->ofm_fd[i]);
238 if (IS_ERR(inf->ofm[i])) {
239 ret = PTR_ERR(inf->ofm[i]);
240 goto put_ofm;
241 }
242
243 inf->ofm_count++;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200244 }
245
246 /* Increment network reference count */
247 ethosu_network_get(net);
248
249 /* Create file descriptor */
250 ret = fd = anon_inode_getfd("ethosu-inference", &ethosu_inference_fops,
251 inf, O_RDWR | O_CLOEXEC);
252 if (ret < 0)
253 goto put_net;
254
255 /* Store pointer to file structure */
256 inf->file = fget(ret);
257 fput(inf->file);
258
259 /* Add inference to inference list */
260 list_add(&inf->list, &edev->inference_list);
261
262 /* Send inference request to Arm Ethos-U subsystem */
263 (void)ethosu_inference_send(inf);
264
265 dev_info(edev->dev, "Inference create. handle=0x%pK, fd=%d",
266 inf, fd);
267
268 return fd;
269
270put_net:
271 ethosu_network_put(inf->net);
Kristofer Jonssonb74492c2020-09-10 13:26:01 +0200272
273put_ofm:
274 while (inf->ofm_count-- > 0)
275 ethosu_buffer_put(inf->ofm[inf->ofm_count]);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200276
277put_ifm:
Kristofer Jonssonb74492c2020-09-10 13:26:01 +0200278 while (inf->ifm_count-- > 0)
279 ethosu_buffer_put(inf->ifm[inf->ifm_count]);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200280
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200281 devm_kfree(edev->dev, inf);
282
283 return ret;
284}
285
286struct ethosu_inference *ethosu_inference_get_from_fd(int fd)
287{
288 struct ethosu_inference *inf;
289 struct file *file;
290
291 file = fget(fd);
292 if (!file)
293 return ERR_PTR(-EINVAL);
294
295 if (!ethosu_inference_verify(file)) {
296 fput(file);
297
298 return ERR_PTR(-EINVAL);
299 }
300
301 inf = file->private_data;
302 ethosu_inference_get(inf);
303 fput(file);
304
305 return inf;
306}
307
308void ethosu_inference_get(struct ethosu_inference *inf)
309{
310 kref_get(&inf->kref);
311}
312
313void ethosu_inference_put(struct ethosu_inference *inf)
314{
315 kref_put(&inf->kref, &ethosu_inference_kref_destroy);
316}
317
318void ethosu_inference_rsp(struct ethosu_device *edev,
319 struct ethosu_core_inference_rsp *rsp)
320{
321 struct ethosu_inference *inf =
322 (struct ethosu_inference *)rsp->user_arg;
323 int ret;
324
325 ret = ethosu_inference_find(inf, &edev->inference_list);
326 if (ret) {
327 dev_warn(edev->dev,
328 "Handle not found in inference list. handle=0x%p\n",
329 rsp);
330
331 return;
332 }
333
334 inf->pending = false;
335
Kristofer Jonssonb74492c2020-09-10 13:26:01 +0200336 if (rsp->status == ETHOSU_CORE_STATUS_OK &&
337 inf->ofm_count <= ETHOSU_CORE_BUFFER_MAX) {
338 uint32_t i;
339
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200340 inf->status = ETHOSU_UAPI_STATUS_OK;
341
Kristofer Jonssonb74492c2020-09-10 13:26:01 +0200342 for (i = 0; i < inf->ofm_count; i++) {
343 struct ethosu_buffer *ofm = inf->ofm[i];
344
345 ret = ethosu_buffer_resize(
346 ofm, ofm->size + rsp->ofm_size[i],
347 ofm->offset);
348 if (ret)
349 inf->status = ETHOSU_UAPI_STATUS_ERROR;
350 }
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200351 } else {
352 inf->status = ETHOSU_UAPI_STATUS_ERROR;
353 }
354
355 wake_up_interruptible(&inf->waitq);
356
357 ethosu_inference_put(inf);
358}