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 18077263fbSStefano Garzarella spinlock_t pkt_list_lock; /* protects pkt_list */ 1971dc9ec9SBobby Eshleman struct sk_buff_head pkt_queue; 20077263fbSStefano Garzarella struct work_struct pkt_work; 21077263fbSStefano Garzarella }; 22077263fbSStefano Garzarella 23077263fbSStefano Garzarella static struct vsock_loopback the_vsock_loopback; 24077263fbSStefano Garzarella 25077263fbSStefano Garzarella static u32 vsock_loopback_get_local_cid(void) 26077263fbSStefano Garzarella { 27077263fbSStefano Garzarella return VMADDR_CID_LOCAL; 28077263fbSStefano Garzarella } 29077263fbSStefano Garzarella 3071dc9ec9SBobby Eshleman static int vsock_loopback_send_pkt(struct sk_buff *skb) 31077263fbSStefano Garzarella { 32077263fbSStefano Garzarella struct vsock_loopback *vsock = &the_vsock_loopback; 3371dc9ec9SBobby Eshleman int len = skb->len; 34077263fbSStefano Garzarella 35077263fbSStefano Garzarella spin_lock_bh(&vsock->pkt_list_lock); 3671dc9ec9SBobby Eshleman skb_queue_tail(&vsock->pkt_queue, skb); 37077263fbSStefano Garzarella spin_unlock_bh(&vsock->pkt_list_lock); 38077263fbSStefano Garzarella 39077263fbSStefano Garzarella queue_work(vsock->workqueue, &vsock->pkt_work); 40077263fbSStefano Garzarella 41077263fbSStefano Garzarella return len; 42077263fbSStefano Garzarella } 43077263fbSStefano Garzarella 44077263fbSStefano Garzarella static int vsock_loopback_cancel_pkt(struct vsock_sock *vsk) 45077263fbSStefano Garzarella { 46077263fbSStefano Garzarella struct vsock_loopback *vsock = &the_vsock_loopback; 47077263fbSStefano Garzarella 4871dc9ec9SBobby Eshleman virtio_transport_purge_skbs(vsk, &vsock->pkt_queue); 49077263fbSStefano Garzarella 50077263fbSStefano Garzarella return 0; 51077263fbSStefano Garzarella } 52077263fbSStefano Garzarella 536e90a577SArseny Krasnov static bool vsock_loopback_seqpacket_allow(u32 remote_cid); 546e90a577SArseny Krasnov 55077263fbSStefano Garzarella static struct virtio_transport loopback_transport = { 56077263fbSStefano Garzarella .transport = { 57077263fbSStefano Garzarella .module = THIS_MODULE, 58077263fbSStefano Garzarella 59077263fbSStefano Garzarella .get_local_cid = vsock_loopback_get_local_cid, 60077263fbSStefano Garzarella 61077263fbSStefano Garzarella .init = virtio_transport_do_socket_init, 62077263fbSStefano Garzarella .destruct = virtio_transport_destruct, 63077263fbSStefano Garzarella .release = virtio_transport_release, 64077263fbSStefano Garzarella .connect = virtio_transport_connect, 65077263fbSStefano Garzarella .shutdown = virtio_transport_shutdown, 66077263fbSStefano Garzarella .cancel_pkt = vsock_loopback_cancel_pkt, 67077263fbSStefano Garzarella 68077263fbSStefano Garzarella .dgram_bind = virtio_transport_dgram_bind, 69077263fbSStefano Garzarella .dgram_dequeue = virtio_transport_dgram_dequeue, 70077263fbSStefano Garzarella .dgram_enqueue = virtio_transport_dgram_enqueue, 71077263fbSStefano Garzarella .dgram_allow = virtio_transport_dgram_allow, 72077263fbSStefano Garzarella 73077263fbSStefano Garzarella .stream_dequeue = virtio_transport_stream_dequeue, 74077263fbSStefano Garzarella .stream_enqueue = virtio_transport_stream_enqueue, 75077263fbSStefano Garzarella .stream_has_data = virtio_transport_stream_has_data, 76077263fbSStefano Garzarella .stream_has_space = virtio_transport_stream_has_space, 77077263fbSStefano Garzarella .stream_rcvhiwat = virtio_transport_stream_rcvhiwat, 78077263fbSStefano Garzarella .stream_is_active = virtio_transport_stream_is_active, 79077263fbSStefano Garzarella .stream_allow = virtio_transport_stream_allow, 80077263fbSStefano Garzarella 816e90a577SArseny Krasnov .seqpacket_dequeue = virtio_transport_seqpacket_dequeue, 826e90a577SArseny Krasnov .seqpacket_enqueue = virtio_transport_seqpacket_enqueue, 836e90a577SArseny Krasnov .seqpacket_allow = vsock_loopback_seqpacket_allow, 846e90a577SArseny Krasnov .seqpacket_has_data = virtio_transport_seqpacket_has_data, 856e90a577SArseny Krasnov 86077263fbSStefano Garzarella .notify_poll_in = virtio_transport_notify_poll_in, 87077263fbSStefano Garzarella .notify_poll_out = virtio_transport_notify_poll_out, 88077263fbSStefano Garzarella .notify_recv_init = virtio_transport_notify_recv_init, 89077263fbSStefano Garzarella .notify_recv_pre_block = virtio_transport_notify_recv_pre_block, 90077263fbSStefano Garzarella .notify_recv_pre_dequeue = virtio_transport_notify_recv_pre_dequeue, 91077263fbSStefano Garzarella .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue, 92077263fbSStefano Garzarella .notify_send_init = virtio_transport_notify_send_init, 93077263fbSStefano Garzarella .notify_send_pre_block = virtio_transport_notify_send_pre_block, 94077263fbSStefano Garzarella .notify_send_pre_enqueue = virtio_transport_notify_send_pre_enqueue, 95077263fbSStefano Garzarella .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue, 96077263fbSStefano Garzarella .notify_buffer_size = virtio_transport_notify_buffer_size, 97*634f1a71SBobby Eshleman 98*634f1a71SBobby Eshleman .read_skb = virtio_transport_read_skb, 99077263fbSStefano Garzarella }, 100077263fbSStefano Garzarella 101077263fbSStefano Garzarella .send_pkt = vsock_loopback_send_pkt, 102077263fbSStefano Garzarella }; 103077263fbSStefano Garzarella 1046e90a577SArseny Krasnov static bool vsock_loopback_seqpacket_allow(u32 remote_cid) 1056e90a577SArseny Krasnov { 1066e90a577SArseny Krasnov return true; 1076e90a577SArseny Krasnov } 1086e90a577SArseny Krasnov 109077263fbSStefano Garzarella static void vsock_loopback_work(struct work_struct *work) 110077263fbSStefano Garzarella { 111077263fbSStefano Garzarella struct vsock_loopback *vsock = 112077263fbSStefano Garzarella container_of(work, struct vsock_loopback, pkt_work); 11371dc9ec9SBobby Eshleman struct sk_buff_head pkts; 11471dc9ec9SBobby Eshleman struct sk_buff *skb; 11571dc9ec9SBobby Eshleman 11671dc9ec9SBobby Eshleman skb_queue_head_init(&pkts); 117077263fbSStefano Garzarella 118077263fbSStefano Garzarella spin_lock_bh(&vsock->pkt_list_lock); 11971dc9ec9SBobby Eshleman skb_queue_splice_init(&vsock->pkt_queue, &pkts); 120077263fbSStefano Garzarella spin_unlock_bh(&vsock->pkt_list_lock); 121077263fbSStefano Garzarella 12271dc9ec9SBobby Eshleman while ((skb = __skb_dequeue(&pkts))) { 12371dc9ec9SBobby Eshleman virtio_transport_deliver_tap_pkt(skb); 12471dc9ec9SBobby Eshleman virtio_transport_recv_pkt(&loopback_transport, skb); 125077263fbSStefano Garzarella } 126077263fbSStefano Garzarella } 127077263fbSStefano Garzarella 128077263fbSStefano Garzarella static int __init vsock_loopback_init(void) 129077263fbSStefano Garzarella { 130077263fbSStefano Garzarella struct vsock_loopback *vsock = &the_vsock_loopback; 131077263fbSStefano Garzarella int ret; 132077263fbSStefano Garzarella 133077263fbSStefano Garzarella vsock->workqueue = alloc_workqueue("vsock-loopback", 0, 0); 134077263fbSStefano Garzarella if (!vsock->workqueue) 135077263fbSStefano Garzarella return -ENOMEM; 136077263fbSStefano Garzarella 137077263fbSStefano Garzarella spin_lock_init(&vsock->pkt_list_lock); 13871dc9ec9SBobby Eshleman skb_queue_head_init(&vsock->pkt_queue); 139077263fbSStefano Garzarella INIT_WORK(&vsock->pkt_work, vsock_loopback_work); 140077263fbSStefano Garzarella 141077263fbSStefano Garzarella ret = vsock_core_register(&loopback_transport.transport, 142077263fbSStefano Garzarella VSOCK_TRANSPORT_F_LOCAL); 143077263fbSStefano Garzarella if (ret) 144077263fbSStefano Garzarella goto out_wq; 145077263fbSStefano Garzarella 146077263fbSStefano Garzarella return 0; 147077263fbSStefano Garzarella 148077263fbSStefano Garzarella out_wq: 149077263fbSStefano Garzarella destroy_workqueue(vsock->workqueue); 150077263fbSStefano Garzarella return ret; 151077263fbSStefano Garzarella } 152077263fbSStefano Garzarella 153077263fbSStefano Garzarella static void __exit vsock_loopback_exit(void) 154077263fbSStefano Garzarella { 155077263fbSStefano Garzarella struct vsock_loopback *vsock = &the_vsock_loopback; 156077263fbSStefano Garzarella 157077263fbSStefano Garzarella vsock_core_unregister(&loopback_transport.transport); 158077263fbSStefano Garzarella 159077263fbSStefano Garzarella flush_work(&vsock->pkt_work); 160077263fbSStefano Garzarella 161077263fbSStefano Garzarella spin_lock_bh(&vsock->pkt_list_lock); 16271dc9ec9SBobby Eshleman virtio_vsock_skb_queue_purge(&vsock->pkt_queue); 163077263fbSStefano Garzarella spin_unlock_bh(&vsock->pkt_list_lock); 164077263fbSStefano Garzarella 165077263fbSStefano Garzarella destroy_workqueue(vsock->workqueue); 166077263fbSStefano Garzarella } 167077263fbSStefano Garzarella 168077263fbSStefano Garzarella module_init(vsock_loopback_init); 169077263fbSStefano Garzarella module_exit(vsock_loopback_exit); 170077263fbSStefano Garzarella MODULE_LICENSE("GPL v2"); 171077263fbSStefano Garzarella MODULE_AUTHOR("Stefano Garzarella <sgarzare@redhat.com>"); 172077263fbSStefano Garzarella MODULE_DESCRIPTION("loopback transport for vsock"); 173077263fbSStefano Garzarella MODULE_ALIAS_NETPROTO(PF_VSOCK); 174