/* * SPDX-FileCopyrightText: Copyright 2020,2022-2023 Arm Limited and/or its affiliates * SPDX-License-Identifier: GPL-2.0-only * * This program is free software and is provided to you under the terms of the * GNU General Public License version 2 as published by the Free Software * Foundation, and any use by you of this program is subject to the terms * of such GNU licence. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you can access it online at * http://www.gnu.org/licenses/gpl-2.0.html. */ /**************************************************************************** * Includes ****************************************************************************/ #include "ethosu_network.h" #include "ethosu_buffer.h" #include "ethosu_device.h" #include "ethosu_inference.h" #include "ethosu_network_info.h" #include "uapi/ethosu.h" #include #include #include #include /**************************************************************************** * Variables ****************************************************************************/ static int ethosu_network_release(struct inode *inode, struct file *file); static long ethosu_network_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static const struct file_operations ethosu_network_fops = { .release = ðosu_network_release, .unlocked_ioctl = ðosu_network_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = ðosu_network_ioctl, #endif }; /**************************************************************************** * Functions ****************************************************************************/ static bool ethosu_network_verify(struct file *file) { return file->f_op == ðosu_network_fops; } static void ethosu_network_destroy(struct kref *kref) { struct ethosu_network *net = container_of(kref, struct ethosu_network, kref); struct device *dev = net->dev; dev_dbg(dev, "Network destroy. net=0x%pK\n", net); if (net->buf != NULL) ethosu_buffer_put(net->buf); memset(net, 0, sizeof(*net)); devm_kfree(dev, net); } static int ethosu_network_release(struct inode *inode, struct file *file) { struct ethosu_network *net = file->private_data; struct device *dev = net->dev; dev_dbg(dev, "Network release. file=0x%pK, net=0x%pK\n", file, net); ethosu_network_put(net); return 0; } static long ethosu_network_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ethosu_network *net = file->private_data; struct device *dev = net->dev; void __user *udata = (void __user *)arg; int ret; ret = device_lock_interruptible(net->dev); if (ret) return ret; switch (cmd) { case ETHOSU_IOCTL_NETWORK_INFO: { struct ethosu_uapi_network_info uapi = { 0 }; dev_dbg(dev, "Network ioctl: Network info. net=0x%pK\n", net); ret = ethosu_network_info_request(dev, net->mailbox, net, &uapi); if (ret) break; ret = copy_to_user(udata, &uapi, sizeof(uapi)) ? -EFAULT : 0; break; } case ETHOSU_IOCTL_INFERENCE_CREATE: { struct ethosu_uapi_inference_create uapi; if (copy_from_user(&uapi, udata, sizeof(uapi))) { ret = -EFAULT; break; } dev_dbg(dev, "Network ioctl: Inference. ifm_fd=%u, ofm_fd=%u\n", uapi.ifm_fd[0], uapi.ofm_fd[0]); ret = ethosu_inference_create(dev, net->mailbox, net, &uapi); break; } default: { dev_err(dev, "Invalid ioctl. cmd=%u, arg=%lu", cmd, arg); ret = -ENOIOCTLCMD; break; } } device_unlock(net->dev); return ret; } int ethosu_network_create(struct device *dev, struct ethosu_mailbox *mailbox, struct ethosu_uapi_network_create *uapi) { struct ethosu_network *net; int ret = -ENOMEM; net = devm_kzalloc(dev, sizeof(*net), GFP_KERNEL); if (!net) return -ENOMEM; net->dev = dev; net->mailbox = mailbox; net->buf = NULL; kref_init(&net->kref); switch (uapi->type) { case ETHOSU_UAPI_NETWORK_BUFFER: net->buf = ethosu_buffer_get_from_fd(uapi->fd); if (IS_ERR(net->buf)) { ret = PTR_ERR(net->buf); goto free_net; } break; case ETHOSU_UAPI_NETWORK_INDEX: net->index = uapi->index; break; default: ret = -EINVAL; goto free_net; } ret = anon_inode_getfd("ethosu-network", ðosu_network_fops, net, O_RDWR | O_CLOEXEC); if (ret < 0) goto put_buf; net->file = fget(ret); fput(net->file); dev_dbg(dev, "Network create. file=0x%pK, fd=%d, net=0x%pK, buf=0x%pK, index=%u", net->file, ret, net, net->buf, net->index); return ret; put_buf: if (net->buf != NULL) ethosu_buffer_put(net->buf); free_net: memset(net, 0, sizeof(*net)); devm_kfree(dev, net); return ret; } struct ethosu_network *ethosu_network_get_from_fd(int fd) { struct ethosu_network *net; struct file *file; file = fget(fd); if (!file) return ERR_PTR(-EINVAL); if (!ethosu_network_verify(file)) { fput(file); return ERR_PTR(-EINVAL); } net = file->private_data; ethosu_network_get(net); fput(file); return net; } void ethosu_network_get(struct ethosu_network *net) { kref_get(&net->kref); } int ethosu_network_put(struct ethosu_network *net) { return kref_put(&net->kref, ethosu_network_destroy); }