2016-07-28 17:36:32 +03:00
/*
* 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>
2017-02-02 21:15:33 +03:00
# include <linux/sched/signal.h>
2016-07-28 17:36:32 +03:00
# 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>
2017-04-21 12:10:46 +03:00
# include <uapi/linux/vsockmon.h>
2016-07-28 17:36:32 +03:00
# 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)
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 ) ;
}
2016-12-06 07:06:06 +03:00
static struct virtio_vsock_pkt *
2016-07-28 17:36:32 +03:00
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 ;
2017-03-15 04:32:14 +03:00
pkt - > vsk = info - > vsk ;
2016-07-28 17:36:32 +03:00
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 ;
}
2017-04-21 12:10:46 +03:00
/* 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 ;
skb = alloc_skb ( sizeof ( * hdr ) + sizeof ( pkt - > hdr ) + pkt - > len ,
GFP_ATOMIC ) ;
if ( ! skb )
return NULL ;
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 15:29:21 +03:00
hdr = skb_put ( skb , sizeof ( * hdr ) ) ;
2017-04-21 12:10:46 +03:00
/* 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 ;
}
2017-06-18 17:52:04 +03:00
skb_put_data ( skb , & pkt - > hdr , sizeof ( pkt - > hdr ) ) ;
2017-04-21 12:10:46 +03:00
if ( pkt - > len ) {
2017-06-18 17:52:04 +03:00
skb_put_data ( skb , pkt - > buf , pkt - > len ) ;
2017-04-21 12:10:46 +03:00
}
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 ) ;
2016-07-28 17:36:32 +03:00
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 ,
2017-03-15 04:32:14 +03:00
. vsk = vsk ,
2016-07-28 17:36:32 +03:00
} ;
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 ,
2017-03-15 04:32:14 +03:00
. vsk = vsk ,
2016-07-28 17:36:32 +03:00
} ;
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 ) ,
2017-03-15 04:32:14 +03:00
. vsk = vsk ,
2016-07-28 17:36:32 +03:00
} ;
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 ,
2017-03-15 04:32:14 +03:00
. vsk = vsk ,
2016-07-28 17:36:32 +03:00
} ;
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 ,
2017-03-15 04:32:14 +03:00
. vsk = vsk ,
2016-07-28 17:36:32 +03:00
} ;
/* 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 ( struct virtio_vsock_pkt * pkt )
{
2019-03-06 13:13:53 +03:00
const struct virtio_transport * t ;
struct virtio_vsock_pkt * reply ;
2016-07-28 17:36:32 +03:00
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 ;
2019-03-06 13:13:53 +03:00
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 )
2016-07-28 17:36:32 +03:00
return - ENOMEM ;
2019-03-06 13:13:53 +03:00
t = virtio_transport_get_ops ( ) ;
if ( ! t ) {
virtio_transport_free_pkt ( reply ) ;
return - ENOTCONN ;
}
return t - > send_pkt ( reply ) ;
2016-07-28 17:36:32 +03:00
}
static void virtio_transport_wait_close ( struct sock * sk , long timeout )
{
if ( timeout ) {
2016-11-11 21:20:50 +03:00
DEFINE_WAIT_FUNC ( wait , woken_wake_function ) ;
add_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2016-07-28 17:36:32 +03:00
do {
if ( sk_wait_event ( sk , & timeout ,
2016-11-11 21:20:50 +03:00
sock_flag ( sk , SOCK_DONE ) , & wait ) )
2016-07-28 17:36:32 +03:00
break ;
} while ( ! signal_pending ( current ) & & timeout ) ;
2016-11-11 21:20:50 +03:00
remove_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2016-07-28 17:36:32 +03:00
}
}
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 )
2017-10-05 23:46:52 +03:00
sk - > sk_state = TCP_CLOSING ;
2016-07-28 17:36:32 +03:00
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 ;
2017-10-05 23:46:52 +03:00
if ( ! ( sk - > sk_state = = TCP_ESTABLISHED | |
sk - > sk_state = = TCP_CLOSING ) )
2016-07-28 17:36:32 +03:00
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 sock * sk = & vsk - > sk ;
bool remove_sock = true ;
lock_sock ( sk ) ;
if ( sk - > sk_type = = SOCK_STREAM )
remove_sock = virtio_transport_close ( vsk ) ;
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 :
2017-10-05 23:46:52 +03:00
sk - > sk_state = TCP_ESTABLISHED ;
2016-07-28 17:36:32 +03:00
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 ) ;
2017-10-05 23:46:52 +03:00
sk - > sk_state = TCP_CLOSE ;
2016-07-28 17:36:32 +03:00
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 )
2017-10-05 23:46:52 +03:00
sk - > sk_state = TCP_CLOSING ;
2016-07-28 17:36:32 +03:00
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 ,
2016-12-06 07:07:15 +03:00
. remote_cid = le64_to_cpu ( pkt - > hdr . src_cid ) ,
2016-07-28 17:36:32 +03:00
. remote_port = le32_to_cpu ( pkt - > hdr . src_port ) ,
. reply = true ,
2017-03-15 04:32:14 +03:00
. vsk = vsk ,
2016-07-28 17:36:32 +03:00
} ;
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 ) ;
2017-10-05 23:46:52 +03:00
child - > sk_state = TCP_ESTABLISHED ;
2016-07-28 17:36:32 +03:00
vchild = vsock_sk ( child ) ;
2016-12-06 07:07:15 +03:00
vsock_addr_init ( & vchild - > local_addr , le64_to_cpu ( pkt - > hdr . dst_cid ) ,
2016-07-28 17:36:32 +03:00
le32_to_cpu ( pkt - > hdr . dst_port ) ) ;
2016-12-06 07:07:15 +03:00
vsock_addr_init ( & vchild - > remote_addr , le64_to_cpu ( pkt - > hdr . src_cid ) ,
2016-07-28 17:36:32 +03:00
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_vsock_pkt * pkt )
{
struct sockaddr_vm src , dst ;
struct vsock_sock * vsk ;
struct sock * sk ;
bool space_available ;
2016-12-06 07:07:15 +03:00
vsock_addr_init ( & src , le64_to_cpu ( pkt - > hdr . src_cid ) ,
2016-07-28 17:36:32 +03:00
le32_to_cpu ( pkt - > hdr . src_port ) ) ;
2016-12-06 07:07:15 +03:00
vsock_addr_init ( & dst , le64_to_cpu ( pkt - > hdr . dst_cid ) ,
2016-07-28 17:36:32 +03:00
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 ( 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 ( pkt ) ;
goto free_pkt ;
}
}
vsk = vsock_sk ( sk ) ;
space_available = virtio_transport_space_update ( sk , pkt ) ;
lock_sock ( sk ) ;
/* 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 ) {
2017-10-05 23:46:52 +03:00
case TCP_LISTEN :
2016-07-28 17:36:32 +03:00
virtio_transport_recv_listen ( sk , pkt ) ;
virtio_transport_free_pkt ( pkt ) ;
break ;
2017-10-05 23:46:52 +03:00
case TCP_SYN_SENT :
2016-07-28 17:36:32 +03:00
virtio_transport_recv_connecting ( sk , pkt ) ;
virtio_transport_free_pkt ( pkt ) ;
break ;
2017-10-05 23:46:52 +03:00
case TCP_ESTABLISHED :
2016-07-28 17:36:32 +03:00
virtio_transport_recv_connected ( sk , pkt ) ;
break ;
2017-10-05 23:46:52 +03:00
case TCP_CLOSING :
2016-07-28 17:36:32 +03:00
virtio_transport_recv_disconnecting ( sk , pkt ) ;
virtio_transport_free_pkt ( pkt ) ;
break ;
default :
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 " ) ;