162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* loopback transport for vsock using virtio_transport_common APIs
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2013-2019 Red Hat, Inc.
562306a36Sopenharmony_ci * Authors: Asias He <asias@redhat.com>
662306a36Sopenharmony_ci *          Stefan Hajnoczi <stefanha@redhat.com>
762306a36Sopenharmony_ci *          Stefano Garzarella <sgarzare@redhat.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/spinlock.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/virtio_vsock.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct vsock_loopback {
1662306a36Sopenharmony_ci	struct workqueue_struct *workqueue;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	struct sk_buff_head pkt_queue;
1962306a36Sopenharmony_ci	struct work_struct pkt_work;
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic struct vsock_loopback the_vsock_loopback;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic u32 vsock_loopback_get_local_cid(void)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	return VMADDR_CID_LOCAL;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int vsock_loopback_send_pkt(struct sk_buff *skb)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct vsock_loopback *vsock = &the_vsock_loopback;
3262306a36Sopenharmony_ci	int len = skb->len;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	virtio_vsock_skb_queue_tail(&vsock->pkt_queue, skb);
3562306a36Sopenharmony_ci	queue_work(vsock->workqueue, &vsock->pkt_work);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return len;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int vsock_loopback_cancel_pkt(struct vsock_sock *vsk)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct vsock_loopback *vsock = &the_vsock_loopback;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	virtio_transport_purge_skbs(vsk, &vsock->pkt_queue);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return 0;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic bool vsock_loopback_seqpacket_allow(u32 remote_cid);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic struct virtio_transport loopback_transport = {
5262306a36Sopenharmony_ci	.transport = {
5362306a36Sopenharmony_ci		.module                   = THIS_MODULE,
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		.get_local_cid            = vsock_loopback_get_local_cid,
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		.init                     = virtio_transport_do_socket_init,
5862306a36Sopenharmony_ci		.destruct                 = virtio_transport_destruct,
5962306a36Sopenharmony_ci		.release                  = virtio_transport_release,
6062306a36Sopenharmony_ci		.connect                  = virtio_transport_connect,
6162306a36Sopenharmony_ci		.shutdown                 = virtio_transport_shutdown,
6262306a36Sopenharmony_ci		.cancel_pkt               = vsock_loopback_cancel_pkt,
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		.dgram_bind               = virtio_transport_dgram_bind,
6562306a36Sopenharmony_ci		.dgram_dequeue            = virtio_transport_dgram_dequeue,
6662306a36Sopenharmony_ci		.dgram_enqueue            = virtio_transport_dgram_enqueue,
6762306a36Sopenharmony_ci		.dgram_allow              = virtio_transport_dgram_allow,
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		.stream_dequeue           = virtio_transport_stream_dequeue,
7062306a36Sopenharmony_ci		.stream_enqueue           = virtio_transport_stream_enqueue,
7162306a36Sopenharmony_ci		.stream_has_data          = virtio_transport_stream_has_data,
7262306a36Sopenharmony_ci		.stream_has_space         = virtio_transport_stream_has_space,
7362306a36Sopenharmony_ci		.stream_rcvhiwat          = virtio_transport_stream_rcvhiwat,
7462306a36Sopenharmony_ci		.stream_is_active         = virtio_transport_stream_is_active,
7562306a36Sopenharmony_ci		.stream_allow             = virtio_transport_stream_allow,
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		.seqpacket_dequeue        = virtio_transport_seqpacket_dequeue,
7862306a36Sopenharmony_ci		.seqpacket_enqueue        = virtio_transport_seqpacket_enqueue,
7962306a36Sopenharmony_ci		.seqpacket_allow          = vsock_loopback_seqpacket_allow,
8062306a36Sopenharmony_ci		.seqpacket_has_data       = virtio_transport_seqpacket_has_data,
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		.notify_poll_in           = virtio_transport_notify_poll_in,
8362306a36Sopenharmony_ci		.notify_poll_out          = virtio_transport_notify_poll_out,
8462306a36Sopenharmony_ci		.notify_recv_init         = virtio_transport_notify_recv_init,
8562306a36Sopenharmony_ci		.notify_recv_pre_block    = virtio_transport_notify_recv_pre_block,
8662306a36Sopenharmony_ci		.notify_recv_pre_dequeue  = virtio_transport_notify_recv_pre_dequeue,
8762306a36Sopenharmony_ci		.notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue,
8862306a36Sopenharmony_ci		.notify_send_init         = virtio_transport_notify_send_init,
8962306a36Sopenharmony_ci		.notify_send_pre_block    = virtio_transport_notify_send_pre_block,
9062306a36Sopenharmony_ci		.notify_send_pre_enqueue  = virtio_transport_notify_send_pre_enqueue,
9162306a36Sopenharmony_ci		.notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue,
9262306a36Sopenharmony_ci		.notify_buffer_size       = virtio_transport_notify_buffer_size,
9362306a36Sopenharmony_ci		.notify_set_rcvlowat      = virtio_transport_notify_set_rcvlowat,
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		.read_skb = virtio_transport_read_skb,
9662306a36Sopenharmony_ci	},
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	.send_pkt = vsock_loopback_send_pkt,
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic bool vsock_loopback_seqpacket_allow(u32 remote_cid)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	return true;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void vsock_loopback_work(struct work_struct *work)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct vsock_loopback *vsock =
10962306a36Sopenharmony_ci		container_of(work, struct vsock_loopback, pkt_work);
11062306a36Sopenharmony_ci	struct sk_buff_head pkts;
11162306a36Sopenharmony_ci	struct sk_buff *skb;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	skb_queue_head_init(&pkts);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	spin_lock_bh(&vsock->pkt_queue.lock);
11662306a36Sopenharmony_ci	skb_queue_splice_init(&vsock->pkt_queue, &pkts);
11762306a36Sopenharmony_ci	spin_unlock_bh(&vsock->pkt_queue.lock);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	while ((skb = __skb_dequeue(&pkts))) {
12062306a36Sopenharmony_ci		virtio_transport_deliver_tap_pkt(skb);
12162306a36Sopenharmony_ci		virtio_transport_recv_pkt(&loopback_transport, skb);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int __init vsock_loopback_init(void)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct vsock_loopback *vsock = &the_vsock_loopback;
12862306a36Sopenharmony_ci	int ret;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	vsock->workqueue = alloc_workqueue("vsock-loopback", 0, 0);
13162306a36Sopenharmony_ci	if (!vsock->workqueue)
13262306a36Sopenharmony_ci		return -ENOMEM;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	skb_queue_head_init(&vsock->pkt_queue);
13562306a36Sopenharmony_ci	INIT_WORK(&vsock->pkt_work, vsock_loopback_work);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	ret = vsock_core_register(&loopback_transport.transport,
13862306a36Sopenharmony_ci				  VSOCK_TRANSPORT_F_LOCAL);
13962306a36Sopenharmony_ci	if (ret)
14062306a36Sopenharmony_ci		goto out_wq;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return 0;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ciout_wq:
14562306a36Sopenharmony_ci	destroy_workqueue(vsock->workqueue);
14662306a36Sopenharmony_ci	return ret;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic void __exit vsock_loopback_exit(void)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct vsock_loopback *vsock = &the_vsock_loopback;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	vsock_core_unregister(&loopback_transport.transport);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	flush_work(&vsock->pkt_work);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	virtio_vsock_skb_queue_purge(&vsock->pkt_queue);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	destroy_workqueue(vsock->workqueue);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cimodule_init(vsock_loopback_init);
16362306a36Sopenharmony_cimodule_exit(vsock_loopback_exit);
16462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
16562306a36Sopenharmony_ciMODULE_AUTHOR("Stefano Garzarella <sgarzare@redhat.com>");
16662306a36Sopenharmony_ciMODULE_DESCRIPTION("loopback transport for vsock");
16762306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_VSOCK);
168