net/vmw_vsock/virtio_transport_common.c

Source file repositories/reference/linux-study-clean/net/vmw_vsock/virtio_transport_common.c

File Facts

System
Linux kernel
Corpus path
net/vmw_vsock/virtio_transport_common.c
Extension
.c
Size
45432 bytes
Lines
1817
Domain
Networking Core
Bucket
Sockets, Protocols, Packet Path, And Network Policy
Inferred role
Networking Core: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Networking stack implementation surface: socket APIs, protocol dispatch, packet flow, routing, filtering, and network namespaces.

Dependency Surface

Detected Declarations

Annotated Snippet

if (!uarg) {
				uarg = msg_zerocopy_realloc(sk_vsock(vsk),
							    pkt_len, NULL, false);
				if (!uarg) {
					virtio_transport_put_credit(vvs, pkt_len);
					return -ENOMEM;
				}

				if (!can_zcopy)
					uarg_to_msgzc(uarg)->zerocopy = 0;

				have_uref = true;
			}
		}
	}

	rest_len = pkt_len;

	do {
		struct sk_buff *skb;
		size_t skb_len;

		skb_len = min(max_skb_len, rest_len);

		skb = virtio_transport_alloc_skb(info, skb_len, can_zcopy,
						 uarg,
						 src_cid, src_port,
						 dst_cid, dst_port);
		if (!skb) {
			ret = -ENOMEM;
			break;
		}

		virtio_transport_inc_tx_pkt(vvs, skb);

		ret = t_ops->send_pkt(skb, info->net);
		if (ret < 0)
			break;

		/* Both virtio and vhost 'send_pkt()' returns 'skb_len',
		 * but for reliability use 'ret' instead of 'skb_len'.
		 * Also if partial send happens (e.g. 'ret' != 'skb_len')
		 * somehow, we break this loop, but account such returned
		 * value in 'virtio_transport_put_credit()'.
		 */
		rest_len -= ret;

		if (WARN_ONCE(ret != skb_len,
			      "'send_pkt()' returns %i, but %zu expected\n",
			      ret, skb_len))
			break;
	} while (rest_len);

	virtio_transport_put_credit(vvs, rest_len);

	/* msg_zerocopy_realloc() initializes the ubuf_info refcnt to 1.
	 * skb_zcopy_set() increases it for each skb, so we can drop that
	 * initial reference to keep it balanced.
	 */
	if (have_uref) {
		if (rest_len == pkt_len)
			/* No data sent, abort the notification. */
			net_zcopy_put_abort(uarg, true);
		else
			net_zcopy_put(uarg);
	}

	/* Return number of bytes, if any data has been sent. */
	if (rest_len != pkt_len)
		ret = pkt_len - rest_len;

	return ret;
}

static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs,
					u32 len)
{
	u64 skb_overhead = (skb_queue_len(&vvs->rx_queue) + 1) * SKB_TRUESIZE(0);

	/* Allow at most buf_alloc * 2 total budget (payload + overhead),
	 * similar to how SO_RCVBUF is doubled to reserve space for sk_buff
	 * metadata. Check payload against buf_alloc to be sure the other
	 * peer is respecting the credit, and sk_buff overhead to bound
	 * queue growth.
	 */
	if ((u64)vvs->buf_used + len > vvs->buf_alloc ||
	    skb_overhead > vvs->buf_alloc)
		return false;

	vvs->rx_bytes += len;

Annotation

Implementation Notes