1077263fbSStefano Garzarella // SPDX-License-Identifier: GPL-2.0-only 2077263fbSStefano Garzarella /* loopback transport for vsock using virtio_transport_common APIs 3077263fbSStefano Garzarella * 4077263fbSStefano Garzarella * Copyright (C) 2013-2019 Red Hat, Inc. 5077263fbSStefano Garzarella * Authors: Asias He <asias@redhat.com> 6077263fbSStefano Garzarella * Stefan Hajnoczi <stefanha@redhat.com> 7077263fbSStefano Garzarella * Stefano Garzarella <sgarzare@redhat.com> 8077263fbSStefano Garzarella * 9077263fbSStefano Garzarella */ 10077263fbSStefano Garzarella #include <linux/spinlock.h> 11077263fbSStefano Garzarella #include <linux/module.h> 12077263fbSStefano Garzarella #include <linux/list.h> 13077263fbSStefano Garzarella #include <linux/virtio_vsock.h> 14077263fbSStefano Garzarella 15077263fbSStefano Garzarella struct vsock_loopback { 16077263fbSStefano Garzarella struct workqueue_struct *workqueue; 17077263fbSStefano Garzarella 1871dc9ec9SBobby Eshleman struct sk_buff_head pkt_queue; 19077263fbSStefano Garzarella struct work_struct pkt_work; 20077263fbSStefano Garzarella }; 21077263fbSStefano Garzarella 22077263fbSStefano Garzarella static struct vsock_loopback the_vsock_loopback; 23077263fbSStefano Garzarella 24077263fbSStefano Garzarella static u32 vsock_loopback_get_local_cid(void) 25077263fbSStefano Garzarella { 26077263fbSStefano Garzarella return VMADDR_CID_LOCAL; 27077263fbSStefano Garzarella } 28077263fbSStefano Garzarella 2971dc9ec9SBobby Eshleman static int vsock_loopback_send_pkt(struct sk_buff *skb) 30077263fbSStefano Garzarella { 31077263fbSStefano Garzarella struct vsock_loopback *vsock = &the_vsock_loopback; 3271dc9ec9SBobby Eshleman int len = skb->len; 33077263fbSStefano Garzarella 3471dc9ec9SBobby Eshleman skb_queue_tail(&vsock->pkt_queue, skb); 35077263fbSStefano Garzarella 36077263fbSStefano Garzarella queue_work(vsock->workqueue, &vsock->pkt_work); 37077263fbSStefano Garzarella 38077263fbSStefano Garzarella return len; 39077263fbSStefano Garzarella } 40077263fbSStefano Garzarella 41077263fbSStefano Garzarella static int vsock_loopback_cancel_pkt(struct vsock_sock *vsk) 42077263fbSStefano Garzarella { 43077263fbSStefano Garzarella struct vsock_loopback *vsock = &the_vsock_loopback; 44077263fbSStefano Garzarella 4571dc9ec9SBobby Eshleman virtio_transport_purge_skbs(vsk, &vsock->pkt_queue); 46077263fbSStefano Garzarella 47077263fbSStefano Garzarella return 0; 48077263fbSStefano Garzarella } 49077263fbSStefano Garzarella 506e90a577SArseny Krasnov static bool vsock_loopback_seqpacket_allow(u32 remote_cid); 516e90a577SArseny Krasnov 52077263fbSStefano Garzarella static struct virtio_transport loopback_transport = { 53077263fbSStefano Garzarella .transport = { 54077263fbSStefano Garzarella .module = THIS_MODULE, 55077263fbSStefano Garzarella 56077263fbSStefano Garzarella .get_local_cid = vsock_loopback_get_local_cid, 57077263fbSStefano Garzarella 58077263fbSStefano Garzarella .init = virtio_transport_do_socket_init, 59077263fbSStefano Garzarella .destruct = virtio_transport_destruct, 60077263fbSStefano Garzarella .release = virtio_transport_release, 61077263fbSStefano Garzarella .connect = virtio_transport_connect, 62077263fbSStefano Garzarella .shutdown = virtio_transport_shutdown, 63077263fbSStefano Garzarella .cancel_pkt = vsock_loopback_cancel_pkt, 64077263fbSStefano Garzarella 65077263fbSStefano Garzarella .dgram_bind = virtio_transport_dgram_bind, 66077263fbSStefano Garzarella .dgram_dequeue = virtio_transport_dgram_dequeue, 67077263fbSStefano Garzarella .dgram_enqueue = virtio_transport_dgram_enqueue, 68077263fbSStefano Garzarella .dgram_allow = virtio_transport_dgram_allow, 69077263fbSStefano Garzarella 70077263fbSStefano Garzarella .stream_dequeue = virtio_transport_stream_dequeue, 71077263fbSStefano Garzarella .stream_enqueue = virtio_transport_stream_enqueue, 72077263fbSStefano Garzarella .stream_has_data = virtio_transport_stream_has_data, 73077263fbSStefano Garzarella .stream_has_space = virtio_transport_stream_has_space, 74077263fbSStefano Garzarella .stream_rcvhiwat = virtio_transport_stream_rcvhiwat, 75077263fbSStefano Garzarella .stream_is_active = virtio_transport_stream_is_active, 76077263fbSStefano Garzarella .stream_allow = virtio_transport_stream_allow, 77077263fbSStefano Garzarella 786e90a577SArseny Krasnov .seqpacket_dequeue = virtio_transport_seqpacket_dequeue, 796e90a577SArseny Krasnov .seqpacket_enqueue = virtio_transport_seqpacket_enqueue, 806e90a577SArseny Krasnov .seqpacket_allow = vsock_loopback_seqpacket_allow, 816e90a577SArseny Krasnov .seqpacket_has_data = virtio_transport_seqpacket_has_data, 826e90a577SArseny Krasnov 83077263fbSStefano Garzarella .notify_poll_in = virtio_transport_notify_poll_in, 84077263fbSStefano Garzarella .notify_poll_out = virtio_transport_notify_poll_out, 85077263fbSStefano Garzarella .notify_recv_init = virtio_transport_notify_recv_init, 86077263fbSStefano Garzarella .notify_recv_pre_block = virtio_transport_notify_recv_pre_block, 87077263fbSStefano Garzarella .notify_recv_pre_dequeue = virtio_transport_notify_recv_pre_dequeue, 88077263fbSStefano Garzarella .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue, 89077263fbSStefano Garzarella .notify_send_init = virtio_transport_notify_send_init, 90077263fbSStefano Garzarella .notify_send_pre_block = virtio_transport_notify_send_pre_block, 91077263fbSStefano Garzarella .notify_send_pre_enqueue = virtio_transport_notify_send_pre_enqueue, 92077263fbSStefano Garzarella .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue, 93077263fbSStefano Garzarella .notify_buffer_size = virtio_transport_notify_buffer_size, 94077263fbSStefano Garzarella }, 95077263fbSStefano Garzarella 96077263fbSStefano Garzarella .send_pkt = vsock_loopback_send_pkt, 97077263fbSStefano Garzarella }; 98077263fbSStefano Garzarella 996e90a577SArseny Krasnov static bool vsock_loopback_seqpacket_allow(u32 remote_cid) 1006e90a577SArseny Krasnov { 1016e90a577SArseny Krasnov return true; 1026e90a577SArseny Krasnov } 1036e90a577SArseny Krasnov 104077263fbSStefano Garzarella static void vsock_loopback_work(struct work_struct *work) 105077263fbSStefano Garzarella { 106077263fbSStefano Garzarella struct vsock_loopback *vsock = 107077263fbSStefano Garzarella container_of(work, struct vsock_loopback, pkt_work); 10871dc9ec9SBobby Eshleman struct sk_buff_head pkts; 10971dc9ec9SBobby Eshleman struct sk_buff *skb; 11071dc9ec9SBobby Eshleman 11171dc9ec9SBobby Eshleman skb_queue_head_init(&pkts); 112077263fbSStefano Garzarella 113*b465518dSStefano Garzarella spin_lock_bh(&vsock->pkt_queue.lock); 11471dc9ec9SBobby Eshleman skb_queue_splice_init(&vsock->pkt_queue, &pkts); 115*b465518dSStefano Garzarella spin_unlock_bh(&vsock->pkt_queue.lock); 116077263fbSStefano Garzarella 11771dc9ec9SBobby Eshleman while ((skb = __skb_dequeue(&pkts))) { 11871dc9ec9SBobby Eshleman virtio_transport_deliver_tap_pkt(skb); 11971dc9ec9SBobby Eshleman virtio_transport_recv_pkt(&loopback_transport, skb); 120077263fbSStefano Garzarella } 121077263fbSStefano Garzarella } 122077263fbSStefano Garzarella 123077263fbSStefano Garzarella static int __init vsock_loopback_init(void) 124077263fbSStefano Garzarella { 125077263fbSStefano Garzarella struct vsock_loopback *vsock = &the_vsock_loopback; 126077263fbSStefano Garzarella int ret; 127077263fbSStefano Garzarella 128077263fbSStefano Garzarella vsock->workqueue = alloc_workqueue("vsock-loopback", 0, 0); 129077263fbSStefano Garzarella if (!vsock->workqueue) 130077263fbSStefano Garzarella return -ENOMEM; 131077263fbSStefano Garzarella 13271dc9ec9SBobby Eshleman skb_queue_head_init(&vsock->pkt_queue); 133077263fbSStefano Garzarella INIT_WORK(&vsock->pkt_work, vsock_loopback_work); 134077263fbSStefano Garzarella 135077263fbSStefano Garzarella ret = vsock_core_register(&loopback_transport.transport, 136077263fbSStefano Garzarella VSOCK_TRANSPORT_F_LOCAL); 137077263fbSStefano Garzarella if (ret) 138077263fbSStefano Garzarella goto out_wq; 139077263fbSStefano Garzarella 140077263fbSStefano Garzarella return 0; 141077263fbSStefano Garzarella 142077263fbSStefano Garzarella out_wq: 143077263fbSStefano Garzarella destroy_workqueue(vsock->workqueue); 144077263fbSStefano Garzarella return ret; 145077263fbSStefano Garzarella } 146077263fbSStefano Garzarella 147077263fbSStefano Garzarella static void __exit vsock_loopback_exit(void) 148077263fbSStefano Garzarella { 149077263fbSStefano Garzarella struct vsock_loopback *vsock = &the_vsock_loopback; 150077263fbSStefano Garzarella 151077263fbSStefano Garzarella vsock_core_unregister(&loopback_transport.transport); 152077263fbSStefano Garzarella 153077263fbSStefano Garzarella flush_work(&vsock->pkt_work); 154077263fbSStefano Garzarella 15571dc9ec9SBobby Eshleman virtio_vsock_skb_queue_purge(&vsock->pkt_queue); 156077263fbSStefano Garzarella 157077263fbSStefano Garzarella destroy_workqueue(vsock->workqueue); 158077263fbSStefano Garzarella } 159077263fbSStefano Garzarella 160077263fbSStefano Garzarella module_init(vsock_loopback_init); 161077263fbSStefano Garzarella module_exit(vsock_loopback_exit); 162077263fbSStefano Garzarella MODULE_LICENSE("GPL v2"); 163077263fbSStefano Garzarella MODULE_AUTHOR("Stefano Garzarella <sgarzare@redhat.com>"); 164077263fbSStefano Garzarella MODULE_DESCRIPTION("loopback transport for vsock"); 165077263fbSStefano Garzarella MODULE_ALIAS_NETPROTO(PF_VSOCK); 166