xref: /openbmc/linux/net/qrtr/tun.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
128fb4e59SBjorn Andersson // SPDX-License-Identifier: GPL-2.0
228fb4e59SBjorn Andersson /* Copyright (c) 2018, Linaro Ltd */
328fb4e59SBjorn Andersson 
428fb4e59SBjorn Andersson #include <linux/miscdevice.h>
528fb4e59SBjorn Andersson #include <linux/module.h>
628fb4e59SBjorn Andersson #include <linux/poll.h>
728fb4e59SBjorn Andersson #include <linux/skbuff.h>
828fb4e59SBjorn Andersson #include <linux/uaccess.h>
928fb4e59SBjorn Andersson 
1028fb4e59SBjorn Andersson #include "qrtr.h"
1128fb4e59SBjorn Andersson 
1228fb4e59SBjorn Andersson struct qrtr_tun {
1328fb4e59SBjorn Andersson 	struct qrtr_endpoint ep;
1428fb4e59SBjorn Andersson 
1528fb4e59SBjorn Andersson 	struct sk_buff_head queue;
1628fb4e59SBjorn Andersson 	wait_queue_head_t readq;
1728fb4e59SBjorn Andersson };
1828fb4e59SBjorn Andersson 
qrtr_tun_send(struct qrtr_endpoint * ep,struct sk_buff * skb)1928fb4e59SBjorn Andersson static int qrtr_tun_send(struct qrtr_endpoint *ep, struct sk_buff *skb)
2028fb4e59SBjorn Andersson {
2128fb4e59SBjorn Andersson 	struct qrtr_tun *tun = container_of(ep, struct qrtr_tun, ep);
2228fb4e59SBjorn Andersson 
2328fb4e59SBjorn Andersson 	skb_queue_tail(&tun->queue, skb);
2428fb4e59SBjorn Andersson 
2528fb4e59SBjorn Andersson 	/* wake up any blocking processes, waiting for new data */
2628fb4e59SBjorn Andersson 	wake_up_interruptible(&tun->readq);
2728fb4e59SBjorn Andersson 
2828fb4e59SBjorn Andersson 	return 0;
2928fb4e59SBjorn Andersson }
3028fb4e59SBjorn Andersson 
qrtr_tun_open(struct inode * inode,struct file * filp)3128fb4e59SBjorn Andersson static int qrtr_tun_open(struct inode *inode, struct file *filp)
3228fb4e59SBjorn Andersson {
3328fb4e59SBjorn Andersson 	struct qrtr_tun *tun;
34*fc0494eaSTakeshi Misawa 	int ret;
3528fb4e59SBjorn Andersson 
3628fb4e59SBjorn Andersson 	tun = kzalloc(sizeof(*tun), GFP_KERNEL);
3728fb4e59SBjorn Andersson 	if (!tun)
3828fb4e59SBjorn Andersson 		return -ENOMEM;
3928fb4e59SBjorn Andersson 
4028fb4e59SBjorn Andersson 	skb_queue_head_init(&tun->queue);
4128fb4e59SBjorn Andersson 	init_waitqueue_head(&tun->readq);
4228fb4e59SBjorn Andersson 
4328fb4e59SBjorn Andersson 	tun->ep.xmit = qrtr_tun_send;
4428fb4e59SBjorn Andersson 
4528fb4e59SBjorn Andersson 	filp->private_data = tun;
4628fb4e59SBjorn Andersson 
47*fc0494eaSTakeshi Misawa 	ret = qrtr_endpoint_register(&tun->ep, QRTR_EP_NID_AUTO);
48*fc0494eaSTakeshi Misawa 	if (ret)
49*fc0494eaSTakeshi Misawa 		goto out;
50*fc0494eaSTakeshi Misawa 
51*fc0494eaSTakeshi Misawa 	return 0;
52*fc0494eaSTakeshi Misawa 
53*fc0494eaSTakeshi Misawa out:
54*fc0494eaSTakeshi Misawa 	filp->private_data = NULL;
55*fc0494eaSTakeshi Misawa 	kfree(tun);
56*fc0494eaSTakeshi Misawa 	return ret;
5728fb4e59SBjorn Andersson }
5828fb4e59SBjorn Andersson 
qrtr_tun_read_iter(struct kiocb * iocb,struct iov_iter * to)5928fb4e59SBjorn Andersson static ssize_t qrtr_tun_read_iter(struct kiocb *iocb, struct iov_iter *to)
6028fb4e59SBjorn Andersson {
6128fb4e59SBjorn Andersson 	struct file *filp = iocb->ki_filp;
6228fb4e59SBjorn Andersson 	struct qrtr_tun *tun = filp->private_data;
6328fb4e59SBjorn Andersson 	struct sk_buff *skb;
6428fb4e59SBjorn Andersson 	int count;
6528fb4e59SBjorn Andersson 
6628fb4e59SBjorn Andersson 	while (!(skb = skb_dequeue(&tun->queue))) {
6728fb4e59SBjorn Andersson 		if (filp->f_flags & O_NONBLOCK)
6828fb4e59SBjorn Andersson 			return -EAGAIN;
6928fb4e59SBjorn Andersson 
7028fb4e59SBjorn Andersson 		/* Wait until we get data or the endpoint goes away */
7128fb4e59SBjorn Andersson 		if (wait_event_interruptible(tun->readq,
7228fb4e59SBjorn Andersson 					     !skb_queue_empty(&tun->queue)))
7328fb4e59SBjorn Andersson 			return -ERESTARTSYS;
7428fb4e59SBjorn Andersson 	}
7528fb4e59SBjorn Andersson 
7628fb4e59SBjorn Andersson 	count = min_t(size_t, iov_iter_count(to), skb->len);
7728fb4e59SBjorn Andersson 	if (copy_to_iter(skb->data, count, to) != count)
7828fb4e59SBjorn Andersson 		count = -EFAULT;
7928fb4e59SBjorn Andersson 
8028fb4e59SBjorn Andersson 	kfree_skb(skb);
8128fb4e59SBjorn Andersson 
8228fb4e59SBjorn Andersson 	return count;
8328fb4e59SBjorn Andersson }
8428fb4e59SBjorn Andersson 
qrtr_tun_write_iter(struct kiocb * iocb,struct iov_iter * from)8528fb4e59SBjorn Andersson static ssize_t qrtr_tun_write_iter(struct kiocb *iocb, struct iov_iter *from)
8628fb4e59SBjorn Andersson {
8728fb4e59SBjorn Andersson 	struct file *filp = iocb->ki_filp;
8828fb4e59SBjorn Andersson 	struct qrtr_tun *tun = filp->private_data;
8928fb4e59SBjorn Andersson 	size_t len = iov_iter_count(from);
9028fb4e59SBjorn Andersson 	ssize_t ret;
9128fb4e59SBjorn Andersson 	void *kbuf;
9228fb4e59SBjorn Andersson 
932a80c158SSabyrzhan Tasbolatov 	if (!len)
942a80c158SSabyrzhan Tasbolatov 		return -EINVAL;
952a80c158SSabyrzhan Tasbolatov 
962a80c158SSabyrzhan Tasbolatov 	if (len > KMALLOC_MAX_SIZE)
972a80c158SSabyrzhan Tasbolatov 		return -ENOMEM;
982a80c158SSabyrzhan Tasbolatov 
9928fb4e59SBjorn Andersson 	kbuf = kzalloc(len, GFP_KERNEL);
10028fb4e59SBjorn Andersson 	if (!kbuf)
10128fb4e59SBjorn Andersson 		return -ENOMEM;
10228fb4e59SBjorn Andersson 
103a21b7f0cSNavid Emamdoost 	if (!copy_from_iter_full(kbuf, len, from)) {
104a21b7f0cSNavid Emamdoost 		kfree(kbuf);
10528fb4e59SBjorn Andersson 		return -EFAULT;
106a21b7f0cSNavid Emamdoost 	}
10728fb4e59SBjorn Andersson 
10828fb4e59SBjorn Andersson 	ret = qrtr_endpoint_post(&tun->ep, kbuf, len);
10928fb4e59SBjorn Andersson 
110a21b7f0cSNavid Emamdoost 	kfree(kbuf);
11128fb4e59SBjorn Andersson 	return ret < 0 ? ret : len;
11228fb4e59SBjorn Andersson }
11328fb4e59SBjorn Andersson 
qrtr_tun_poll(struct file * filp,poll_table * wait)11428fb4e59SBjorn Andersson static __poll_t qrtr_tun_poll(struct file *filp, poll_table *wait)
11528fb4e59SBjorn Andersson {
11628fb4e59SBjorn Andersson 	struct qrtr_tun *tun = filp->private_data;
11728fb4e59SBjorn Andersson 	__poll_t mask = 0;
11828fb4e59SBjorn Andersson 
11928fb4e59SBjorn Andersson 	poll_wait(filp, &tun->readq, wait);
12028fb4e59SBjorn Andersson 
12128fb4e59SBjorn Andersson 	if (!skb_queue_empty(&tun->queue))
12228fb4e59SBjorn Andersson 		mask |= EPOLLIN | EPOLLRDNORM;
12328fb4e59SBjorn Andersson 
12428fb4e59SBjorn Andersson 	return mask;
12528fb4e59SBjorn Andersson }
12628fb4e59SBjorn Andersson 
qrtr_tun_release(struct inode * inode,struct file * filp)12728fb4e59SBjorn Andersson static int qrtr_tun_release(struct inode *inode, struct file *filp)
12828fb4e59SBjorn Andersson {
12928fb4e59SBjorn Andersson 	struct qrtr_tun *tun = filp->private_data;
13028fb4e59SBjorn Andersson 
13128fb4e59SBjorn Andersson 	qrtr_endpoint_unregister(&tun->ep);
13228fb4e59SBjorn Andersson 
13328fb4e59SBjorn Andersson 	/* Discard all SKBs */
13421d8bd12SChristophe JAILLET 	skb_queue_purge(&tun->queue);
13528fb4e59SBjorn Andersson 
13628fb4e59SBjorn Andersson 	kfree(tun);
13728fb4e59SBjorn Andersson 
13828fb4e59SBjorn Andersson 	return 0;
13928fb4e59SBjorn Andersson }
14028fb4e59SBjorn Andersson 
14128fb4e59SBjorn Andersson static const struct file_operations qrtr_tun_ops = {
14228fb4e59SBjorn Andersson 	.owner = THIS_MODULE,
14328fb4e59SBjorn Andersson 	.open = qrtr_tun_open,
14428fb4e59SBjorn Andersson 	.poll = qrtr_tun_poll,
14528fb4e59SBjorn Andersson 	.read_iter = qrtr_tun_read_iter,
14628fb4e59SBjorn Andersson 	.write_iter = qrtr_tun_write_iter,
14728fb4e59SBjorn Andersson 	.release = qrtr_tun_release,
14828fb4e59SBjorn Andersson };
14928fb4e59SBjorn Andersson 
15028fb4e59SBjorn Andersson static struct miscdevice qrtr_tun_miscdev = {
15128fb4e59SBjorn Andersson 	MISC_DYNAMIC_MINOR,
15228fb4e59SBjorn Andersson 	"qrtr-tun",
15328fb4e59SBjorn Andersson 	&qrtr_tun_ops,
15428fb4e59SBjorn Andersson };
15528fb4e59SBjorn Andersson 
qrtr_tun_init(void)15628fb4e59SBjorn Andersson static int __init qrtr_tun_init(void)
15728fb4e59SBjorn Andersson {
15828fb4e59SBjorn Andersson 	int ret;
15928fb4e59SBjorn Andersson 
16028fb4e59SBjorn Andersson 	ret = misc_register(&qrtr_tun_miscdev);
16128fb4e59SBjorn Andersson 	if (ret)
16228fb4e59SBjorn Andersson 		pr_err("failed to register Qualcomm IPC Router tun device\n");
16328fb4e59SBjorn Andersson 
16428fb4e59SBjorn Andersson 	return ret;
16528fb4e59SBjorn Andersson }
16628fb4e59SBjorn Andersson 
qrtr_tun_exit(void)16728fb4e59SBjorn Andersson static void __exit qrtr_tun_exit(void)
16828fb4e59SBjorn Andersson {
16928fb4e59SBjorn Andersson 	misc_deregister(&qrtr_tun_miscdev);
17028fb4e59SBjorn Andersson }
17128fb4e59SBjorn Andersson 
17228fb4e59SBjorn Andersson module_init(qrtr_tun_init);
17328fb4e59SBjorn Andersson module_exit(qrtr_tun_exit);
17428fb4e59SBjorn Andersson 
17528fb4e59SBjorn Andersson MODULE_DESCRIPTION("Qualcomm IPC Router TUN device");
17628fb4e59SBjorn Andersson MODULE_LICENSE("GPL v2");
177