/* The following fields are protected by rx_lock. vqs[VSOCK_VQ_RX] *mustbeaccessedwithrx_lockheld.
*/ struct mutex rx_lock; bool rx_run; int rx_buf_nr; int rx_buf_max_nr;
/* The following fields are protected by event_lock. *vqs[VSOCK_VQ_EVENT]mustbeaccessedwithevent_lockheld.
*/ struct mutex event_lock; bool event_run; struct virtio_vsock_event event_list[8];
u32 guest_cid; bool seqpacket_allow;
/* These fields are used only in tx path in function *'virtio_transport_send_pkt_work()',sotosave *stackspaceinit,placebothofthemhere.Each *pointerfrom'out_sgs'pointstothecorresponding *elementin'out_bufs'-thisisinitializedin *'virtio_vsock_probe()'.Bothfieldsareprotected *by'tx_lock'.+1isneededforpacketheader.
*/ struct scatterlist *out_sgs[MAX_SKB_FRAGS + 1]; struct scatterlist out_bufs[MAX_SKB_FRAGS + 1];
};
if (!skb_is_nonlinear(skb)) { if (skb->len > 0) {
sg_init_one(sgs[out_sg], skb->data, skb->len);
out_sg++;
}
} else { struct skb_shared_info *si; int i;
/* If skb is nonlinear, then its buffer must contain *onlyheaderandnothingmore.Dataisstoredin *thefraggedpart.
*/
WARN_ON_ONCE(skb_headroom(skb) != sizeof(*virtio_vsock_hdr(skb)));
si = skb_shinfo(skb);
for (i = 0; i < si->nr_frags; i++) {
skb_frag_t *skb_frag = &si->frags[i]; void *va;
/* We will use 'page_to_virt()' for the userspace page *here,becausevirtioordma-mappinglayerswillcall *'virt_to_phys()'latertofillthebufferdescriptor. *Wedon'ttouchmemoryat"virtual"addressofthispage.
*/
va = page_to_virt(skb_frag_page(skb_frag));
sg_init_one(sgs[out_sg],
va + skb_frag_off(skb_frag),
skb_frag_size(skb_frag));
out_sg++;
}
}
ret = virtqueue_add_sgs(vq, sgs, out_sg, in_sg, skb, gfp); /* Usually this means that there is no more space available in *thevq
*/ if (ret < 0) return ret;
for (;;) { struct sk_buff *skb; bool reply; int ret;
skb = virtio_vsock_skb_dequeue(&vsock->send_pkt_queue); if (!skb) break;
reply = virtio_vsock_skb_reply(skb);
ret = virtio_transport_send_skb(skb, vq, vsock, GFP_KERNEL); if (ret < 0) {
virtio_vsock_skb_queue_head(&vsock->send_pkt_queue, skb); break;
}
if (reply) { struct virtqueue *rx_vq = vsock->vqs[VSOCK_VQ_RX]; int val;
val = atomic_dec_return(&vsock->queued_replies);
/* Do we now have resources to resume rx processing? */ if (val + 1 == virtqueue_get_vring_size(rx_vq))
restart_rx = true;
}
added = true;
}
if (added)
virtqueue_kick(vq);
out:
mutex_unlock(&vsock->tx_lock);
if (restart_rx)
queue_work(virtio_vsock_workqueue, &vsock->rx_work);
}
/* Caller need to hold RCU for vsock. *Returns0ifthepacketissuccessfullyputonthevq.
*/ staticint virtio_transport_send_skb_fast_path(struct virtio_vsock *vsock, struct sk_buff *skb)
{ struct virtqueue *vq = vsock->vqs[VSOCK_VQ_TX]; int ret;
/* Inside RCU, can't sleep! */
ret = mutex_trylock(&vsock->tx_lock); if (unlikely(ret == 0)) return -EBUSY;
ret = virtio_transport_send_skb(skb, vq, vsock, GFP_ATOMIC); if (ret == 0)
virtqueue_kick(vq);
mutex_unlock(&vsock->tx_lock);
return ret;
}
staticint
virtio_transport_send_pkt(struct sk_buff *skb)
{ struct virtio_vsock_hdr *hdr; struct virtio_vsock *vsock; int len = skb->len;
hdr = virtio_vsock_hdr(skb);
rcu_read_lock();
vsock = rcu_dereference(the_virtio_vsock); if (!vsock) {
kfree_skb(skb);
len = -ENODEV; goto out_rcu;
}
if (le64_to_cpu(hdr->dst_cid) == vsock->guest_cid) {
kfree_skb(skb);
len = -ENODEV; goto out_rcu;
}
/* If send_pkt_queue is empty, we can safely bypass this queue *becausepacketorderismaintainedand(try)toputthepacket *onthevirtqueueusingvirtio_transport_send_skb_fast_path. *Ifthisfailswesimplyputthepacketontheintermediate *queueandscheduletheworker.
*/ if (!skb_queue_empty_lockless(&vsock->send_pkt_queue) ||
virtio_transport_send_skb_fast_path(vsock, skb)) { if (virtio_vsock_skb_reply(skb))
atomic_inc(&vsock->queued_replies);
virtqueue_disable_cb(vq); while ((skb = virtqueue_get_buf(vq, &len)) != NULL) {
virtio_transport_consume_skb_sent(skb, true);
added = true;
}
} while (!virtqueue_enable_cb(vq));
out:
mutex_unlock(&vsock->tx_lock);
if (added)
queue_work(virtio_vsock_workqueue, &vsock->send_pkt_work);
}
/* Is there space left for replies to rx packets? */ staticbool virtio_transport_more_replies(struct virtio_vsock *vsock)
{ struct virtqueue *vq = vsock->vqs[VSOCK_VQ_RX]; int val;
smp_rmb(); /* paired with atomic_inc() and atomic_dec_return() */
val = atomic_read(&vsock->queued_replies);
return val < virtqueue_get_vring_size(vq);
}
/* event_lock must be held */ staticint virtio_vsock_event_fill_one(struct virtio_vsock *vsock, struct virtio_vsock_event *event)
{ struct scatterlist sg; struct virtqueue *vq;
/* event_lock must be held */ staticvoid virtio_vsock_event_fill(struct virtio_vsock *vsock)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(vsock->event_list); i++) { struct virtio_vsock_event *event = &vsock->event_list[i];
virtio_vsock_event_fill_one(vsock, event);
}
virtqueue_kick(vsock->vqs[VSOCK_VQ_EVENT]);
}
staticvoid virtio_vsock_reset_sock(struct sock *sk)
{ /* vmci_transport.c doesn't take sk_lock here either. At least we're *undervsock_table_locksothesockcannotdisappearwhilewe're *executing.
*/
/* Check that tx queue is large enough to keep whole *datatosend.Thisisneeded,becausewhenthereis *notenoughfreespaceinthequeue,currentskbto *sendwillbereinsertedtotheheadoftxlistof *thesockettoretrytransmissionlater,soifskb *isbiggerthanwholequeue,itwillbereinserted *againandagain,thusblockingotherskbstobesent. *Eachpageoftheuserprovidedbufferwillbeadded *asasinglebuffertothetxvirtqueue,socompare *numberofpagesagainstmaximumcapacityofthequeue.
*/ if (bufs_num <= vq->num_max)
res = true;
}
/* Reset all connected sockets when the VQs disappear */
vsock_for_each_connected_socket(&virtio_transport.transport,
virtio_vsock_reset_sock);
/* Stop all work handlers to make sure no one is accessing the device, *sowecansafelycallvirtio_reset_device().
*/
mutex_lock(&vsock->rx_lock);
vsock->rx_run = false;
mutex_unlock(&vsock->rx_lock);
/* Delete virtqueues and flush outstanding callbacks if any */
vdev->config->del_vqs(vdev);
}
staticint virtio_vsock_probe(struct virtio_device *vdev)
{ struct virtio_vsock *vsock = NULL; int ret; int i;
ret = mutex_lock_interruptible(&the_virtio_vsock_mutex); if (ret) return ret;
/* Only one virtio-vsock device per guest is supported */ if (rcu_dereference_protected(the_virtio_vsock,
lockdep_is_held(&the_virtio_vsock_mutex))) {
ret = -EBUSY; goto out;
}
vsock = kzalloc(sizeof(*vsock), GFP_KERNEL); if (!vsock) {
ret = -ENOMEM; goto out;
}
/* Other works can be queued before 'config->del_vqs()', so we flush *allworksbeforetofreethevsockobjecttoavoiduseafterfree.
*/
flush_work(&vsock->rx_work);
flush_work(&vsock->tx_work);
flush_work(&vsock->event_work);
flush_work(&vsock->send_pkt_work);
/* Only one virtio-vsock device per guest is supported */ if (rcu_dereference_protected(the_virtio_vsock,
lockdep_is_held(&the_virtio_vsock_mutex))) {
ret = -EBUSY; goto out;
}
ret = virtio_vsock_vqs_init(vsock); if (ret < 0) goto out;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.