forked from mirrors/linux
		
	vsock/virtio: support to send non-linear skb
For non-linear skb use its pages from fragment array as buffers in virtio tx queue. These pages are already pinned by 'get_user_pages()' during such skb creation. Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com> Reviewed-by: Stefano Garzarella <sgarzare@redhat.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
		
							parent
							
								
									0df7cd3c13
								
							
						
					
					
						commit
						64c99d2d6a
					
				
					 1 changed files with 52 additions and 6 deletions
				
			
		|  | @ -63,6 +63,17 @@ struct virtio_vsock { | ||||||
| 
 | 
 | ||||||
| 	u32 guest_cid; | 	u32 guest_cid; | ||||||
| 	bool seqpacket_allow; | 	bool seqpacket_allow; | ||||||
|  | 
 | ||||||
|  | 	/* These fields are used only in tx path in function
 | ||||||
|  | 	 * 'virtio_transport_send_pkt_work()', so to save | ||||||
|  | 	 * stack space in it, place both of them here. Each | ||||||
|  | 	 * pointer from 'out_sgs' points to the corresponding | ||||||
|  | 	 * element in 'out_bufs' - this is initialized in | ||||||
|  | 	 * 'virtio_vsock_probe()'. Both fields are protected | ||||||
|  | 	 * by 'tx_lock'. +1 is needed for packet header. | ||||||
|  | 	 */ | ||||||
|  | 	struct scatterlist *out_sgs[MAX_SKB_FRAGS + 1]; | ||||||
|  | 	struct scatterlist out_bufs[MAX_SKB_FRAGS + 1]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static u32 virtio_transport_get_local_cid(void) | static u32 virtio_transport_get_local_cid(void) | ||||||
|  | @ -100,8 +111,8 @@ virtio_transport_send_pkt_work(struct work_struct *work) | ||||||
| 	vq = vsock->vqs[VSOCK_VQ_TX]; | 	vq = vsock->vqs[VSOCK_VQ_TX]; | ||||||
| 
 | 
 | ||||||
| 	for (;;) { | 	for (;;) { | ||||||
| 		struct scatterlist hdr, buf, *sgs[2]; |  | ||||||
| 		int ret, in_sg = 0, out_sg = 0; | 		int ret, in_sg = 0, out_sg = 0; | ||||||
|  | 		struct scatterlist **sgs; | ||||||
| 		struct sk_buff *skb; | 		struct sk_buff *skb; | ||||||
| 		bool reply; | 		bool reply; | ||||||
| 
 | 
 | ||||||
|  | @ -111,12 +122,43 @@ virtio_transport_send_pkt_work(struct work_struct *work) | ||||||
| 
 | 
 | ||||||
| 		virtio_transport_deliver_tap_pkt(skb); | 		virtio_transport_deliver_tap_pkt(skb); | ||||||
| 		reply = virtio_vsock_skb_reply(skb); | 		reply = virtio_vsock_skb_reply(skb); | ||||||
|  | 		sgs = vsock->out_sgs; | ||||||
|  | 		sg_init_one(sgs[out_sg], virtio_vsock_hdr(skb), | ||||||
|  | 			    sizeof(*virtio_vsock_hdr(skb))); | ||||||
|  | 		out_sg++; | ||||||
| 
 | 
 | ||||||
| 		sg_init_one(&hdr, virtio_vsock_hdr(skb), sizeof(*virtio_vsock_hdr(skb))); | 		if (!skb_is_nonlinear(skb)) { | ||||||
| 		sgs[out_sg++] = &hdr; |  | ||||||
| 			if (skb->len > 0) { | 			if (skb->len > 0) { | ||||||
| 			sg_init_one(&buf, skb->data, skb->len); | 				sg_init_one(sgs[out_sg], skb->data, skb->len); | ||||||
| 			sgs[out_sg++] = &buf; | 				out_sg++; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			struct skb_shared_info *si; | ||||||
|  | 			int i; | ||||||
|  | 
 | ||||||
|  | 			/* If skb is nonlinear, then its buffer must contain
 | ||||||
|  | 			 * only header and nothing more. Data is stored in | ||||||
|  | 			 * the fragged part. | ||||||
|  | 			 */ | ||||||
|  | 			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, because virtio or dma-mapping layers will call | ||||||
|  | 				 * 'virt_to_phys()' later to fill the buffer descriptor. | ||||||
|  | 				 * We don't touch memory at "virtual" address of this page. | ||||||
|  | 				 */ | ||||||
|  | 				va = page_to_virt(skb_frag->bv_page); | ||||||
|  | 				sg_init_one(sgs[out_sg], | ||||||
|  | 					    va + skb_frag->bv_offset, | ||||||
|  | 					    skb_frag->bv_len); | ||||||
|  | 				out_sg++; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		ret = virtqueue_add_sgs(vq, sgs, out_sg, in_sg, skb, GFP_KERNEL); | 		ret = virtqueue_add_sgs(vq, sgs, out_sg, in_sg, skb, GFP_KERNEL); | ||||||
|  | @ -621,6 +663,7 @@ static int virtio_vsock_probe(struct virtio_device *vdev) | ||||||
| { | { | ||||||
| 	struct virtio_vsock *vsock = NULL; | 	struct virtio_vsock *vsock = NULL; | ||||||
| 	int ret; | 	int ret; | ||||||
|  | 	int i; | ||||||
| 
 | 
 | ||||||
| 	ret = mutex_lock_interruptible(&the_virtio_vsock_mutex); | 	ret = mutex_lock_interruptible(&the_virtio_vsock_mutex); | ||||||
| 	if (ret) | 	if (ret) | ||||||
|  | @ -663,6 +706,9 @@ static int virtio_vsock_probe(struct virtio_device *vdev) | ||||||
| 	if (ret < 0) | 	if (ret < 0) | ||||||
| 		goto out; | 		goto out; | ||||||
| 
 | 
 | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(vsock->out_sgs); i++) | ||||||
|  | 		vsock->out_sgs[i] = &vsock->out_bufs[i]; | ||||||
|  | 
 | ||||||
| 	rcu_assign_pointer(the_virtio_vsock, vsock); | 	rcu_assign_pointer(the_virtio_vsock, vsock); | ||||||
| 
 | 
 | ||||||
| 	mutex_unlock(&the_virtio_vsock_mutex); | 	mutex_unlock(&the_virtio_vsock_mutex); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Arseniy Krasnov
						Arseniy Krasnov