diff options
-rw-r--r-- | net/vmw_vsock/virtio_transport.c | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index 936d7eee62d0..2e47f9f06b96 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -44,6 +44,10 @@ struct virtio_vsock { spinlock_t send_pkt_list_lock; struct list_head send_pkt_list; + struct work_struct loopback_work; + spinlock_t loopback_list_lock; /* protects loopback_list */ + struct list_head loopback_list; + atomic_t queued_replies; /* The following fields are protected by rx_lock. vqs[VSOCK_VQ_RX] @@ -74,6 +78,42 @@ static u32 virtio_transport_get_local_cid(void) return vsock->guest_cid; } +static void virtio_transport_loopback_work(struct work_struct *work) +{ + struct virtio_vsock *vsock = + container_of(work, struct virtio_vsock, loopback_work); + LIST_HEAD(pkts); + + spin_lock_bh(&vsock->loopback_list_lock); + list_splice_init(&vsock->loopback_list, &pkts); + spin_unlock_bh(&vsock->loopback_list_lock); + + mutex_lock(&vsock->rx_lock); + while (!list_empty(&pkts)) { + struct virtio_vsock_pkt *pkt; + + pkt = list_first_entry(&pkts, struct virtio_vsock_pkt, list); + list_del_init(&pkt->list); + + virtio_transport_recv_pkt(pkt); + } + mutex_unlock(&vsock->rx_lock); +} + +static int virtio_transport_send_pkt_loopback(struct virtio_vsock *vsock, + struct virtio_vsock_pkt *pkt) +{ + int len = pkt->len; + + spin_lock_bh(&vsock->loopback_list_lock); + list_add_tail(&pkt->list, &vsock->loopback_list); + spin_unlock_bh(&vsock->loopback_list_lock); + + queue_work(virtio_vsock_workqueue, &vsock->loopback_work); + + return len; +} + static void virtio_transport_send_pkt_work(struct work_struct *work) { @@ -159,6 +199,9 @@ virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt) return -ENODEV; } + if (le32_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid) + return virtio_transport_send_pkt_loopback(vsock, pkt); + if (pkt->reply) atomic_inc(&vsock->queued_replies); @@ -510,10 +553,13 @@ static int virtio_vsock_probe(struct virtio_device *vdev) mutex_init(&vsock->event_lock); spin_lock_init(&vsock->send_pkt_list_lock); INIT_LIST_HEAD(&vsock->send_pkt_list); + spin_lock_init(&vsock->loopback_list_lock); + INIT_LIST_HEAD(&vsock->loopback_list); INIT_WORK(&vsock->rx_work, virtio_transport_rx_work); INIT_WORK(&vsock->tx_work, virtio_transport_tx_work); INIT_WORK(&vsock->event_work, virtio_transport_event_work); INIT_WORK(&vsock->send_pkt_work, virtio_transport_send_pkt_work); + INIT_WORK(&vsock->loopback_work, virtio_transport_loopback_work); mutex_lock(&vsock->rx_lock); virtio_vsock_rx_fill(vsock); @@ -539,6 +585,7 @@ static void virtio_vsock_remove(struct virtio_device *vdev) struct virtio_vsock *vsock = vdev->priv; struct virtio_vsock_pkt *pkt; + flush_work(&vsock->loopback_work); flush_work(&vsock->rx_work); flush_work(&vsock->tx_work); flush_work(&vsock->event_work); @@ -565,6 +612,15 @@ static void virtio_vsock_remove(struct virtio_device *vdev) } spin_unlock_bh(&vsock->send_pkt_list_lock); + spin_lock_bh(&vsock->loopback_list_lock); + while (!list_empty(&vsock->loopback_list)) { + pkt = list_first_entry(&vsock->loopback_list, + struct virtio_vsock_pkt, list); + list_del(&pkt->list); + virtio_transport_free_pkt(pkt); + } + spin_unlock_bh(&vsock->loopback_list_lock); + mutex_lock(&the_virtio_vsock_mutex); the_virtio_vsock = NULL; vsock_core_exit(); |