1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090 |
- /*
- * common code for virtio vsock
- *
- * Copyright (C) 2013-2015 Red Hat, Inc.
- * Author: Asias He <asias@redhat.com>
- * Stefan Hajnoczi <stefanha@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
- */
- #include <linux/spinlock.h>
- #include <linux/module.h>
- #include <linux/sched/signal.h>
- #include <linux/ctype.h>
- #include <linux/list.h>
- #include <linux/virtio.h>
- #include <linux/virtio_ids.h>
- #include <linux/virtio_config.h>
- #include <linux/virtio_vsock.h>
- #include <uapi/linux/vsockmon.h>
- #include <net/sock.h>
- #include <net/af_vsock.h>
- #define CREATE_TRACE_POINTS
- #include <trace/events/vsock_virtio_transport_common.h>
- /* How long to wait for graceful shutdown of a connection */
- #define VSOCK_CLOSE_TIMEOUT (8 * HZ)
- uint virtio_transport_max_vsock_pkt_buf_size = 64 * 1024;
- module_param(virtio_transport_max_vsock_pkt_buf_size, uint, 0444);
- EXPORT_SYMBOL_GPL(virtio_transport_max_vsock_pkt_buf_size);
- static const struct virtio_transport *virtio_transport_get_ops(void)
- {
- const struct vsock_transport *t = vsock_core_get_transport();
- return container_of(t, struct virtio_transport, transport);
- }
- static struct virtio_vsock_pkt *
- virtio_transport_alloc_pkt(struct virtio_vsock_pkt_info *info,
- size_t len,
- u32 src_cid,
- u32 src_port,
- u32 dst_cid,
- u32 dst_port)
- {
- struct virtio_vsock_pkt *pkt;
- int err;
- pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
- if (!pkt)
- return NULL;
- pkt->hdr.type = cpu_to_le16(info->type);
- pkt->hdr.op = cpu_to_le16(info->op);
- pkt->hdr.src_cid = cpu_to_le64(src_cid);
- pkt->hdr.dst_cid = cpu_to_le64(dst_cid);
- pkt->hdr.src_port = cpu_to_le32(src_port);
- pkt->hdr.dst_port = cpu_to_le32(dst_port);
- pkt->hdr.flags = cpu_to_le32(info->flags);
- pkt->len = len;
- pkt->hdr.len = cpu_to_le32(len);
- pkt->reply = info->reply;
- pkt->vsk = info->vsk;
- if (info->msg && len > 0) {
- pkt->buf = kmalloc(len, GFP_KERNEL);
- if (!pkt->buf)
- goto out_pkt;
- err = memcpy_from_msg(pkt->buf, info->msg, len);
- if (err)
- goto out;
- }
- trace_virtio_transport_alloc_pkt(src_cid, src_port,
- dst_cid, dst_port,
- len,
- info->type,
- info->op,
- info->flags);
- return pkt;
- out:
- kfree(pkt->buf);
- out_pkt:
- kfree(pkt);
- return NULL;
- }
- /* Packet capture */
- static struct sk_buff *virtio_transport_build_skb(void *opaque)
- {
- struct virtio_vsock_pkt *pkt = opaque;
- struct af_vsockmon_hdr *hdr;
- struct sk_buff *skb;
- size_t payload_len;
- void *payload_buf;
- /* A packet could be split to fit the RX buffer, so we can retrieve
- * the payload length from the header and the buffer pointer taking
- * care of the offset in the original packet.
- */
- payload_len = le32_to_cpu(pkt->hdr.len);
- payload_buf = pkt->buf + pkt->off;
- skb = alloc_skb(sizeof(*hdr) + sizeof(pkt->hdr) + payload_len,
- GFP_ATOMIC);
- if (!skb)
- return NULL;
- hdr = skb_put(skb, sizeof(*hdr));
- /* pkt->hdr is little-endian so no need to byteswap here */
- hdr->src_cid = pkt->hdr.src_cid;
- hdr->src_port = pkt->hdr.src_port;
- hdr->dst_cid = pkt->hdr.dst_cid;
- hdr->dst_port = pkt->hdr.dst_port;
- hdr->transport = cpu_to_le16(AF_VSOCK_TRANSPORT_VIRTIO);
- hdr->len = cpu_to_le16(sizeof(pkt->hdr));
- memset(hdr->reserved, 0, sizeof(hdr->reserved));
- switch (le16_to_cpu(pkt->hdr.op)) {
- case VIRTIO_VSOCK_OP_REQUEST:
- case VIRTIO_VSOCK_OP_RESPONSE:
- hdr->op = cpu_to_le16(AF_VSOCK_OP_CONNECT);
- break;
- case VIRTIO_VSOCK_OP_RST:
- case VIRTIO_VSOCK_OP_SHUTDOWN:
- hdr->op = cpu_to_le16(AF_VSOCK_OP_DISCONNECT);
- break;
- case VIRTIO_VSOCK_OP_RW:
- hdr->op = cpu_to_le16(AF_VSOCK_OP_PAYLOAD);
- break;
- case VIRTIO_VSOCK_OP_CREDIT_UPDATE:
- case VIRTIO_VSOCK_OP_CREDIT_REQUEST:
- hdr->op = cpu_to_le16(AF_VSOCK_OP_CONTROL);
- break;
- default:
- hdr->op = cpu_to_le16(AF_VSOCK_OP_UNKNOWN);
- break;
- }
- skb_put_data(skb, &pkt->hdr, sizeof(pkt->hdr));
- if (payload_len) {
- skb_put_data(skb, payload_buf, payload_len);
- }
- return skb;
- }
- void virtio_transport_deliver_tap_pkt(struct virtio_vsock_pkt *pkt)
- {
- vsock_deliver_tap(virtio_transport_build_skb, pkt);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_deliver_tap_pkt);
- static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
- struct virtio_vsock_pkt_info *info)
- {
- u32 src_cid, src_port, dst_cid, dst_port;
- struct virtio_vsock_sock *vvs;
- struct virtio_vsock_pkt *pkt;
- u32 pkt_len = info->pkt_len;
- src_cid = vm_sockets_get_local_cid();
- src_port = vsk->local_addr.svm_port;
- if (!info->remote_cid) {
- dst_cid = vsk->remote_addr.svm_cid;
- dst_port = vsk->remote_addr.svm_port;
- } else {
- dst_cid = info->remote_cid;
- dst_port = info->remote_port;
- }
- vvs = vsk->trans;
- /* we can send less than pkt_len bytes */
- if (pkt_len > VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE)
- pkt_len = VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE;
- /* virtio_transport_get_credit might return less than pkt_len credit */
- pkt_len = virtio_transport_get_credit(vvs, pkt_len);
- /* Do not send zero length OP_RW pkt */
- if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW)
- return pkt_len;
- pkt = virtio_transport_alloc_pkt(info, pkt_len,
- src_cid, src_port,
- dst_cid, dst_port);
- if (!pkt) {
- virtio_transport_put_credit(vvs, pkt_len);
- return -ENOMEM;
- }
- virtio_transport_inc_tx_pkt(vvs, pkt);
- return virtio_transport_get_ops()->send_pkt(pkt);
- }
- static void virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs,
- struct virtio_vsock_pkt *pkt)
- {
- vvs->rx_bytes += pkt->len;
- }
- static void virtio_transport_dec_rx_pkt(struct virtio_vsock_sock *vvs,
- struct virtio_vsock_pkt *pkt)
- {
- vvs->rx_bytes -= pkt->len;
- vvs->fwd_cnt += pkt->len;
- }
- void virtio_transport_inc_tx_pkt(struct virtio_vsock_sock *vvs, struct virtio_vsock_pkt *pkt)
- {
- spin_lock_bh(&vvs->tx_lock);
- pkt->hdr.fwd_cnt = cpu_to_le32(vvs->fwd_cnt);
- pkt->hdr.buf_alloc = cpu_to_le32(vvs->buf_alloc);
- spin_unlock_bh(&vvs->tx_lock);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_inc_tx_pkt);
- u32 virtio_transport_get_credit(struct virtio_vsock_sock *vvs, u32 credit)
- {
- u32 ret;
- spin_lock_bh(&vvs->tx_lock);
- ret = vvs->peer_buf_alloc - (vvs->tx_cnt - vvs->peer_fwd_cnt);
- if (ret > credit)
- ret = credit;
- vvs->tx_cnt += ret;
- spin_unlock_bh(&vvs->tx_lock);
- return ret;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_get_credit);
- void virtio_transport_put_credit(struct virtio_vsock_sock *vvs, u32 credit)
- {
- spin_lock_bh(&vvs->tx_lock);
- vvs->tx_cnt -= credit;
- spin_unlock_bh(&vvs->tx_lock);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_put_credit);
- static int virtio_transport_send_credit_update(struct vsock_sock *vsk,
- int type,
- struct virtio_vsock_hdr *hdr)
- {
- struct virtio_vsock_pkt_info info = {
- .op = VIRTIO_VSOCK_OP_CREDIT_UPDATE,
- .type = type,
- .vsk = vsk,
- };
- return virtio_transport_send_pkt_info(vsk, &info);
- }
- static ssize_t
- virtio_transport_stream_do_dequeue(struct vsock_sock *vsk,
- struct msghdr *msg,
- size_t len)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- struct virtio_vsock_pkt *pkt;
- size_t bytes, total = 0;
- int err = -EFAULT;
- spin_lock_bh(&vvs->rx_lock);
- while (total < len && !list_empty(&vvs->rx_queue)) {
- pkt = list_first_entry(&vvs->rx_queue,
- struct virtio_vsock_pkt, list);
- bytes = len - total;
- if (bytes > pkt->len - pkt->off)
- bytes = pkt->len - pkt->off;
- /* sk_lock is held by caller so no one else can dequeue.
- * Unlock rx_lock since memcpy_to_msg() may sleep.
- */
- spin_unlock_bh(&vvs->rx_lock);
- err = memcpy_to_msg(msg, pkt->buf + pkt->off, bytes);
- if (err)
- goto out;
- spin_lock_bh(&vvs->rx_lock);
- total += bytes;
- pkt->off += bytes;
- if (pkt->off == pkt->len) {
- virtio_transport_dec_rx_pkt(vvs, pkt);
- list_del(&pkt->list);
- virtio_transport_free_pkt(pkt);
- }
- }
- spin_unlock_bh(&vvs->rx_lock);
- /* Send a credit pkt to peer */
- virtio_transport_send_credit_update(vsk, VIRTIO_VSOCK_TYPE_STREAM,
- NULL);
- return total;
- out:
- if (total)
- err = total;
- return err;
- }
- ssize_t
- virtio_transport_stream_dequeue(struct vsock_sock *vsk,
- struct msghdr *msg,
- size_t len, int flags)
- {
- if (flags & MSG_PEEK)
- return -EOPNOTSUPP;
- return virtio_transport_stream_do_dequeue(vsk, msg, len);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_stream_dequeue);
- int
- virtio_transport_dgram_dequeue(struct vsock_sock *vsk,
- struct msghdr *msg,
- size_t len, int flags)
- {
- return -EOPNOTSUPP;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_dgram_dequeue);
- s64 virtio_transport_stream_has_data(struct vsock_sock *vsk)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- s64 bytes;
- spin_lock_bh(&vvs->rx_lock);
- bytes = vvs->rx_bytes;
- spin_unlock_bh(&vvs->rx_lock);
- return bytes;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_stream_has_data);
- static s64 virtio_transport_has_space(struct vsock_sock *vsk)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- s64 bytes;
- bytes = vvs->peer_buf_alloc - (vvs->tx_cnt - vvs->peer_fwd_cnt);
- if (bytes < 0)
- bytes = 0;
- return bytes;
- }
- s64 virtio_transport_stream_has_space(struct vsock_sock *vsk)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- s64 bytes;
- spin_lock_bh(&vvs->tx_lock);
- bytes = virtio_transport_has_space(vsk);
- spin_unlock_bh(&vvs->tx_lock);
- return bytes;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_stream_has_space);
- int virtio_transport_do_socket_init(struct vsock_sock *vsk,
- struct vsock_sock *psk)
- {
- struct virtio_vsock_sock *vvs;
- vvs = kzalloc(sizeof(*vvs), GFP_KERNEL);
- if (!vvs)
- return -ENOMEM;
- vsk->trans = vvs;
- vvs->vsk = vsk;
- if (psk) {
- struct virtio_vsock_sock *ptrans = psk->trans;
- vvs->buf_size = ptrans->buf_size;
- vvs->buf_size_min = ptrans->buf_size_min;
- vvs->buf_size_max = ptrans->buf_size_max;
- vvs->peer_buf_alloc = ptrans->peer_buf_alloc;
- } else {
- vvs->buf_size = VIRTIO_VSOCK_DEFAULT_BUF_SIZE;
- vvs->buf_size_min = VIRTIO_VSOCK_DEFAULT_MIN_BUF_SIZE;
- vvs->buf_size_max = VIRTIO_VSOCK_DEFAULT_MAX_BUF_SIZE;
- }
- vvs->buf_alloc = vvs->buf_size;
- spin_lock_init(&vvs->rx_lock);
- spin_lock_init(&vvs->tx_lock);
- INIT_LIST_HEAD(&vvs->rx_queue);
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_do_socket_init);
- u64 virtio_transport_get_buffer_size(struct vsock_sock *vsk)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- return vvs->buf_size;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_get_buffer_size);
- u64 virtio_transport_get_min_buffer_size(struct vsock_sock *vsk)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- return vvs->buf_size_min;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_get_min_buffer_size);
- u64 virtio_transport_get_max_buffer_size(struct vsock_sock *vsk)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- return vvs->buf_size_max;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_get_max_buffer_size);
- void virtio_transport_set_buffer_size(struct vsock_sock *vsk, u64 val)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- if (val > VIRTIO_VSOCK_MAX_BUF_SIZE)
- val = VIRTIO_VSOCK_MAX_BUF_SIZE;
- if (val < vvs->buf_size_min)
- vvs->buf_size_min = val;
- if (val > vvs->buf_size_max)
- vvs->buf_size_max = val;
- vvs->buf_size = val;
- vvs->buf_alloc = val;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_set_buffer_size);
- void virtio_transport_set_min_buffer_size(struct vsock_sock *vsk, u64 val)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- if (val > VIRTIO_VSOCK_MAX_BUF_SIZE)
- val = VIRTIO_VSOCK_MAX_BUF_SIZE;
- if (val > vvs->buf_size)
- vvs->buf_size = val;
- vvs->buf_size_min = val;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_set_min_buffer_size);
- void virtio_transport_set_max_buffer_size(struct vsock_sock *vsk, u64 val)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- if (val > VIRTIO_VSOCK_MAX_BUF_SIZE)
- val = VIRTIO_VSOCK_MAX_BUF_SIZE;
- if (val < vvs->buf_size)
- vvs->buf_size = val;
- vvs->buf_size_max = val;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_set_max_buffer_size);
- int
- virtio_transport_notify_poll_in(struct vsock_sock *vsk,
- size_t target,
- bool *data_ready_now)
- {
- if (vsock_stream_has_data(vsk))
- *data_ready_now = true;
- else
- *data_ready_now = false;
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_notify_poll_in);
- int
- virtio_transport_notify_poll_out(struct vsock_sock *vsk,
- size_t target,
- bool *space_avail_now)
- {
- s64 free_space;
- free_space = vsock_stream_has_space(vsk);
- if (free_space > 0)
- *space_avail_now = true;
- else if (free_space == 0)
- *space_avail_now = false;
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_notify_poll_out);
- int virtio_transport_notify_recv_init(struct vsock_sock *vsk,
- size_t target, struct vsock_transport_recv_notify_data *data)
- {
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_notify_recv_init);
- int virtio_transport_notify_recv_pre_block(struct vsock_sock *vsk,
- size_t target, struct vsock_transport_recv_notify_data *data)
- {
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_notify_recv_pre_block);
- int virtio_transport_notify_recv_pre_dequeue(struct vsock_sock *vsk,
- size_t target, struct vsock_transport_recv_notify_data *data)
- {
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_notify_recv_pre_dequeue);
- int virtio_transport_notify_recv_post_dequeue(struct vsock_sock *vsk,
- size_t target, ssize_t copied, bool data_read,
- struct vsock_transport_recv_notify_data *data)
- {
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_notify_recv_post_dequeue);
- int virtio_transport_notify_send_init(struct vsock_sock *vsk,
- struct vsock_transport_send_notify_data *data)
- {
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_notify_send_init);
- int virtio_transport_notify_send_pre_block(struct vsock_sock *vsk,
- struct vsock_transport_send_notify_data *data)
- {
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_notify_send_pre_block);
- int virtio_transport_notify_send_pre_enqueue(struct vsock_sock *vsk,
- struct vsock_transport_send_notify_data *data)
- {
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_notify_send_pre_enqueue);
- int virtio_transport_notify_send_post_enqueue(struct vsock_sock *vsk,
- ssize_t written, struct vsock_transport_send_notify_data *data)
- {
- return 0;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_notify_send_post_enqueue);
- u64 virtio_transport_stream_rcvhiwat(struct vsock_sock *vsk)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- return vvs->buf_size;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_stream_rcvhiwat);
- bool virtio_transport_stream_is_active(struct vsock_sock *vsk)
- {
- return true;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_stream_is_active);
- bool virtio_transport_stream_allow(u32 cid, u32 port)
- {
- return true;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_stream_allow);
- int virtio_transport_dgram_bind(struct vsock_sock *vsk,
- struct sockaddr_vm *addr)
- {
- return -EOPNOTSUPP;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_dgram_bind);
- bool virtio_transport_dgram_allow(u32 cid, u32 port)
- {
- return false;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_dgram_allow);
- int virtio_transport_connect(struct vsock_sock *vsk)
- {
- struct virtio_vsock_pkt_info info = {
- .op = VIRTIO_VSOCK_OP_REQUEST,
- .type = VIRTIO_VSOCK_TYPE_STREAM,
- .vsk = vsk,
- };
- return virtio_transport_send_pkt_info(vsk, &info);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_connect);
- int virtio_transport_shutdown(struct vsock_sock *vsk, int mode)
- {
- struct virtio_vsock_pkt_info info = {
- .op = VIRTIO_VSOCK_OP_SHUTDOWN,
- .type = VIRTIO_VSOCK_TYPE_STREAM,
- .flags = (mode & RCV_SHUTDOWN ?
- VIRTIO_VSOCK_SHUTDOWN_RCV : 0) |
- (mode & SEND_SHUTDOWN ?
- VIRTIO_VSOCK_SHUTDOWN_SEND : 0),
- .vsk = vsk,
- };
- return virtio_transport_send_pkt_info(vsk, &info);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_shutdown);
- int
- virtio_transport_dgram_enqueue(struct vsock_sock *vsk,
- struct sockaddr_vm *remote_addr,
- struct msghdr *msg,
- size_t dgram_len)
- {
- return -EOPNOTSUPP;
- }
- EXPORT_SYMBOL_GPL(virtio_transport_dgram_enqueue);
- ssize_t
- virtio_transport_stream_enqueue(struct vsock_sock *vsk,
- struct msghdr *msg,
- size_t len)
- {
- struct virtio_vsock_pkt_info info = {
- .op = VIRTIO_VSOCK_OP_RW,
- .type = VIRTIO_VSOCK_TYPE_STREAM,
- .msg = msg,
- .pkt_len = len,
- .vsk = vsk,
- };
- return virtio_transport_send_pkt_info(vsk, &info);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_stream_enqueue);
- void virtio_transport_destruct(struct vsock_sock *vsk)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- kfree(vvs);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_destruct);
- static int virtio_transport_reset(struct vsock_sock *vsk,
- struct virtio_vsock_pkt *pkt)
- {
- struct virtio_vsock_pkt_info info = {
- .op = VIRTIO_VSOCK_OP_RST,
- .type = VIRTIO_VSOCK_TYPE_STREAM,
- .reply = !!pkt,
- .vsk = vsk,
- };
- /* Send RST only if the original pkt is not a RST pkt */
- if (pkt && le16_to_cpu(pkt->hdr.op) == VIRTIO_VSOCK_OP_RST)
- return 0;
- return virtio_transport_send_pkt_info(vsk, &info);
- }
- /* Normally packets are associated with a socket. There may be no socket if an
- * attempt was made to connect to a socket that does not exist.
- */
- static int virtio_transport_reset_no_sock(const struct virtio_transport *t,
- struct virtio_vsock_pkt *pkt)
- {
- struct virtio_vsock_pkt *reply;
- struct virtio_vsock_pkt_info info = {
- .op = VIRTIO_VSOCK_OP_RST,
- .type = le16_to_cpu(pkt->hdr.type),
- .reply = true,
- };
- /* Send RST only if the original pkt is not a RST pkt */
- if (le16_to_cpu(pkt->hdr.op) == VIRTIO_VSOCK_OP_RST)
- return 0;
- reply = virtio_transport_alloc_pkt(&info, 0,
- le64_to_cpu(pkt->hdr.dst_cid),
- le32_to_cpu(pkt->hdr.dst_port),
- le64_to_cpu(pkt->hdr.src_cid),
- le32_to_cpu(pkt->hdr.src_port));
- if (!reply)
- return -ENOMEM;
- if (!t) {
- virtio_transport_free_pkt(reply);
- return -ENOTCONN;
- }
- return t->send_pkt(reply);
- }
- static void virtio_transport_wait_close(struct sock *sk, long timeout)
- {
- if (timeout) {
- DEFINE_WAIT_FUNC(wait, woken_wake_function);
- add_wait_queue(sk_sleep(sk), &wait);
- do {
- if (sk_wait_event(sk, &timeout,
- sock_flag(sk, SOCK_DONE), &wait))
- break;
- } while (!signal_pending(current) && timeout);
- remove_wait_queue(sk_sleep(sk), &wait);
- }
- }
- static void virtio_transport_do_close(struct vsock_sock *vsk,
- bool cancel_timeout)
- {
- struct sock *sk = sk_vsock(vsk);
- sock_set_flag(sk, SOCK_DONE);
- vsk->peer_shutdown = SHUTDOWN_MASK;
- if (vsock_stream_has_data(vsk) <= 0)
- sk->sk_state = TCP_CLOSING;
- sk->sk_state_change(sk);
- if (vsk->close_work_scheduled &&
- (!cancel_timeout || cancel_delayed_work(&vsk->close_work))) {
- vsk->close_work_scheduled = false;
- vsock_remove_sock(vsk);
- /* Release refcnt obtained when we scheduled the timeout */
- sock_put(sk);
- }
- }
- static void virtio_transport_close_timeout(struct work_struct *work)
- {
- struct vsock_sock *vsk =
- container_of(work, struct vsock_sock, close_work.work);
- struct sock *sk = sk_vsock(vsk);
- sock_hold(sk);
- lock_sock(sk);
- if (!sock_flag(sk, SOCK_DONE)) {
- (void)virtio_transport_reset(vsk, NULL);
- virtio_transport_do_close(vsk, false);
- }
- vsk->close_work_scheduled = false;
- release_sock(sk);
- sock_put(sk);
- }
- /* User context, vsk->sk is locked */
- static bool virtio_transport_close(struct vsock_sock *vsk)
- {
- struct sock *sk = &vsk->sk;
- if (!(sk->sk_state == TCP_ESTABLISHED ||
- sk->sk_state == TCP_CLOSING))
- return true;
- /* Already received SHUTDOWN from peer, reply with RST */
- if ((vsk->peer_shutdown & SHUTDOWN_MASK) == SHUTDOWN_MASK) {
- (void)virtio_transport_reset(vsk, NULL);
- return true;
- }
- if ((sk->sk_shutdown & SHUTDOWN_MASK) != SHUTDOWN_MASK)
- (void)virtio_transport_shutdown(vsk, SHUTDOWN_MASK);
- if (sock_flag(sk, SOCK_LINGER) && !(current->flags & PF_EXITING))
- virtio_transport_wait_close(sk, sk->sk_lingertime);
- if (sock_flag(sk, SOCK_DONE)) {
- return true;
- }
- sock_hold(sk);
- INIT_DELAYED_WORK(&vsk->close_work,
- virtio_transport_close_timeout);
- vsk->close_work_scheduled = true;
- schedule_delayed_work(&vsk->close_work, VSOCK_CLOSE_TIMEOUT);
- return false;
- }
- void virtio_transport_release(struct vsock_sock *vsk)
- {
- struct virtio_vsock_sock *vvs = vsk->trans;
- struct virtio_vsock_pkt *pkt, *tmp;
- struct sock *sk = &vsk->sk;
- bool remove_sock = true;
- lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
- if (sk->sk_type == SOCK_STREAM)
- remove_sock = virtio_transport_close(vsk);
- list_for_each_entry_safe(pkt, tmp, &vvs->rx_queue, list) {
- list_del(&pkt->list);
- virtio_transport_free_pkt(pkt);
- }
- release_sock(sk);
- if (remove_sock)
- vsock_remove_sock(vsk);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_release);
- static int
- virtio_transport_recv_connecting(struct sock *sk,
- struct virtio_vsock_pkt *pkt)
- {
- struct vsock_sock *vsk = vsock_sk(sk);
- int err;
- int skerr;
- switch (le16_to_cpu(pkt->hdr.op)) {
- case VIRTIO_VSOCK_OP_RESPONSE:
- sk->sk_state = TCP_ESTABLISHED;
- sk->sk_socket->state = SS_CONNECTED;
- vsock_insert_connected(vsk);
- sk->sk_state_change(sk);
- break;
- case VIRTIO_VSOCK_OP_INVALID:
- break;
- case VIRTIO_VSOCK_OP_RST:
- skerr = ECONNRESET;
- err = 0;
- goto destroy;
- default:
- skerr = EPROTO;
- err = -EINVAL;
- goto destroy;
- }
- return 0;
- destroy:
- virtio_transport_reset(vsk, pkt);
- sk->sk_state = TCP_CLOSE;
- sk->sk_err = skerr;
- sk->sk_error_report(sk);
- return err;
- }
- static int
- virtio_transport_recv_connected(struct sock *sk,
- struct virtio_vsock_pkt *pkt)
- {
- struct vsock_sock *vsk = vsock_sk(sk);
- struct virtio_vsock_sock *vvs = vsk->trans;
- int err = 0;
- switch (le16_to_cpu(pkt->hdr.op)) {
- case VIRTIO_VSOCK_OP_RW:
- pkt->len = le32_to_cpu(pkt->hdr.len);
- pkt->off = 0;
- spin_lock_bh(&vvs->rx_lock);
- virtio_transport_inc_rx_pkt(vvs, pkt);
- list_add_tail(&pkt->list, &vvs->rx_queue);
- spin_unlock_bh(&vvs->rx_lock);
- sk->sk_data_ready(sk);
- return err;
- case VIRTIO_VSOCK_OP_CREDIT_UPDATE:
- sk->sk_write_space(sk);
- break;
- case VIRTIO_VSOCK_OP_SHUTDOWN:
- if (le32_to_cpu(pkt->hdr.flags) & VIRTIO_VSOCK_SHUTDOWN_RCV)
- vsk->peer_shutdown |= RCV_SHUTDOWN;
- if (le32_to_cpu(pkt->hdr.flags) & VIRTIO_VSOCK_SHUTDOWN_SEND)
- vsk->peer_shutdown |= SEND_SHUTDOWN;
- if (vsk->peer_shutdown == SHUTDOWN_MASK &&
- vsock_stream_has_data(vsk) <= 0)
- sk->sk_state = TCP_CLOSING;
- if (le32_to_cpu(pkt->hdr.flags))
- sk->sk_state_change(sk);
- break;
- case VIRTIO_VSOCK_OP_RST:
- virtio_transport_do_close(vsk, true);
- break;
- default:
- err = -EINVAL;
- break;
- }
- virtio_transport_free_pkt(pkt);
- return err;
- }
- static void
- virtio_transport_recv_disconnecting(struct sock *sk,
- struct virtio_vsock_pkt *pkt)
- {
- struct vsock_sock *vsk = vsock_sk(sk);
- if (le16_to_cpu(pkt->hdr.op) == VIRTIO_VSOCK_OP_RST)
- virtio_transport_do_close(vsk, true);
- }
- static int
- virtio_transport_send_response(struct vsock_sock *vsk,
- struct virtio_vsock_pkt *pkt)
- {
- struct virtio_vsock_pkt_info info = {
- .op = VIRTIO_VSOCK_OP_RESPONSE,
- .type = VIRTIO_VSOCK_TYPE_STREAM,
- .remote_cid = le64_to_cpu(pkt->hdr.src_cid),
- .remote_port = le32_to_cpu(pkt->hdr.src_port),
- .reply = true,
- .vsk = vsk,
- };
- return virtio_transport_send_pkt_info(vsk, &info);
- }
- /* Handle server socket */
- static int
- virtio_transport_recv_listen(struct sock *sk, struct virtio_vsock_pkt *pkt)
- {
- struct vsock_sock *vsk = vsock_sk(sk);
- struct vsock_sock *vchild;
- struct sock *child;
- if (le16_to_cpu(pkt->hdr.op) != VIRTIO_VSOCK_OP_REQUEST) {
- virtio_transport_reset(vsk, pkt);
- return -EINVAL;
- }
- if (sk_acceptq_is_full(sk)) {
- virtio_transport_reset(vsk, pkt);
- return -ENOMEM;
- }
- child = __vsock_create(sock_net(sk), NULL, sk, GFP_KERNEL,
- sk->sk_type, 0);
- if (!child) {
- virtio_transport_reset(vsk, pkt);
- return -ENOMEM;
- }
- sk->sk_ack_backlog++;
- lock_sock_nested(child, SINGLE_DEPTH_NESTING);
- child->sk_state = TCP_ESTABLISHED;
- vchild = vsock_sk(child);
- vsock_addr_init(&vchild->local_addr, le64_to_cpu(pkt->hdr.dst_cid),
- le32_to_cpu(pkt->hdr.dst_port));
- vsock_addr_init(&vchild->remote_addr, le64_to_cpu(pkt->hdr.src_cid),
- le32_to_cpu(pkt->hdr.src_port));
- vsock_insert_connected(vchild);
- vsock_enqueue_accept(sk, child);
- virtio_transport_send_response(vchild, pkt);
- release_sock(child);
- sk->sk_data_ready(sk);
- return 0;
- }
- static bool virtio_transport_space_update(struct sock *sk,
- struct virtio_vsock_pkt *pkt)
- {
- struct vsock_sock *vsk = vsock_sk(sk);
- struct virtio_vsock_sock *vvs = vsk->trans;
- bool space_available;
- /* buf_alloc and fwd_cnt is always included in the hdr */
- spin_lock_bh(&vvs->tx_lock);
- vvs->peer_buf_alloc = le32_to_cpu(pkt->hdr.buf_alloc);
- vvs->peer_fwd_cnt = le32_to_cpu(pkt->hdr.fwd_cnt);
- space_available = virtio_transport_has_space(vsk);
- spin_unlock_bh(&vvs->tx_lock);
- return space_available;
- }
- /* We are under the virtio-vsock's vsock->rx_lock or vhost-vsock's vq->mutex
- * lock.
- */
- void virtio_transport_recv_pkt(struct virtio_transport *t,
- struct virtio_vsock_pkt *pkt)
- {
- struct sockaddr_vm src, dst;
- struct vsock_sock *vsk;
- struct sock *sk;
- bool space_available;
- vsock_addr_init(&src, le64_to_cpu(pkt->hdr.src_cid),
- le32_to_cpu(pkt->hdr.src_port));
- vsock_addr_init(&dst, le64_to_cpu(pkt->hdr.dst_cid),
- le32_to_cpu(pkt->hdr.dst_port));
- trace_virtio_transport_recv_pkt(src.svm_cid, src.svm_port,
- dst.svm_cid, dst.svm_port,
- le32_to_cpu(pkt->hdr.len),
- le16_to_cpu(pkt->hdr.type),
- le16_to_cpu(pkt->hdr.op),
- le32_to_cpu(pkt->hdr.flags),
- le32_to_cpu(pkt->hdr.buf_alloc),
- le32_to_cpu(pkt->hdr.fwd_cnt));
- if (le16_to_cpu(pkt->hdr.type) != VIRTIO_VSOCK_TYPE_STREAM) {
- (void)virtio_transport_reset_no_sock(t, pkt);
- goto free_pkt;
- }
- /* The socket must be in connected or bound table
- * otherwise send reset back
- */
- sk = vsock_find_connected_socket(&src, &dst);
- if (!sk) {
- sk = vsock_find_bound_socket(&dst);
- if (!sk) {
- (void)virtio_transport_reset_no_sock(t, pkt);
- goto free_pkt;
- }
- }
- vsk = vsock_sk(sk);
- lock_sock(sk);
- space_available = virtio_transport_space_update(sk, pkt);
- /* Update CID in case it has changed after a transport reset event */
- vsk->local_addr.svm_cid = dst.svm_cid;
- if (space_available)
- sk->sk_write_space(sk);
- switch (sk->sk_state) {
- case TCP_LISTEN:
- virtio_transport_recv_listen(sk, pkt);
- virtio_transport_free_pkt(pkt);
- break;
- case TCP_SYN_SENT:
- virtio_transport_recv_connecting(sk, pkt);
- virtio_transport_free_pkt(pkt);
- break;
- case TCP_ESTABLISHED:
- virtio_transport_recv_connected(sk, pkt);
- break;
- case TCP_CLOSING:
- virtio_transport_recv_disconnecting(sk, pkt);
- virtio_transport_free_pkt(pkt);
- break;
- default:
- (void)virtio_transport_reset_no_sock(t, pkt);
- virtio_transport_free_pkt(pkt);
- break;
- }
- release_sock(sk);
- /* Release refcnt obtained when we fetched this socket out of the
- * bound or connected list.
- */
- sock_put(sk);
- return;
- free_pkt:
- virtio_transport_free_pkt(pkt);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_recv_pkt);
- void virtio_transport_free_pkt(struct virtio_vsock_pkt *pkt)
- {
- kfree(pkt->buf);
- kfree(pkt);
- }
- EXPORT_SYMBOL_GPL(virtio_transport_free_pkt);
- MODULE_LICENSE("GPL v2");
- MODULE_AUTHOR("Asias He");
- MODULE_DESCRIPTION("common code for virtio vsock");
|