2017-08-26 07:52:43 +03:00
/*
* Hyper - V transport for vsock
*
* Hyper - V Sockets supplies a byte - stream based communication mechanism
* between the host and the VM . This driver implements the necessary
* support in the VM by introducing the new vsock transport .
*
* Copyright ( c ) 2017 , Microsoft Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
*/
# include <linux/module.h>
# include <linux/vmalloc.h>
# include <linux/hyperv.h>
# include <net/sock.h>
# include <net/af_vsock.h>
/* The host side's design of the feature requires 6 exact 4KB pages for
* recv / send rings respectively - - this is suboptimal considering memory
* consumption , however unluckily we have to live with it , before the
* host comes up with a better design in the future .
*/
# define PAGE_SIZE_4K 4096
# define RINGBUFFER_HVS_RCV_SIZE (PAGE_SIZE_4K * 6)
# define RINGBUFFER_HVS_SND_SIZE (PAGE_SIZE_4K * 6)
/* The MTU is 16KB per the host side's design */
# define HVS_MTU_SIZE (1024 * 16)
struct vmpipe_proto_header {
u32 pkt_type ;
u32 data_size ;
} ;
/* For recv, we use the VMBus in-place packet iterator APIs to directly copy
* data from the ringbuffer into the userspace buffer .
*/
struct hvs_recv_buf {
/* The header before the payload data */
struct vmpipe_proto_header hdr ;
/* The payload */
u8 data [ HVS_MTU_SIZE ] ;
} ;
/* We can send up to HVS_MTU_SIZE bytes of payload to the host, but let's use
* a small size , i . e . HVS_SEND_BUF_SIZE , to minimize the dynamically - allocated
* buffer , because tests show there is no significant performance difference .
*
* Note : the buffer can be eliminated in the future when we add new VMBus
* ringbuffer APIs that allow us to directly copy data from userspace buffer
* to VMBus ringbuffer .
*/
# define HVS_SEND_BUF_SIZE (PAGE_SIZE_4K - sizeof(struct vmpipe_proto_header))
struct hvs_send_buf {
/* The header before the payload data */
struct vmpipe_proto_header hdr ;
/* The payload */
u8 data [ HVS_SEND_BUF_SIZE ] ;
} ;
# define HVS_HEADER_LEN (sizeof(struct vmpacket_descriptor) + \
sizeof ( struct vmpipe_proto_header ) )
/* See 'prev_indices' in hv_ringbuffer_read(), hv_ringbuffer_write(), and
* __hv_pkt_iter_next ( ) .
*/
# define VMBUS_PKT_TRAILER_SIZE (sizeof(u64))
# define HVS_PKT_LEN(payload_len) (HVS_HEADER_LEN + \
ALIGN ( ( payload_len ) , 8 ) + \
VMBUS_PKT_TRAILER_SIZE )
union hvs_service_id {
uuid_le srv_id ;
struct {
unsigned int svm_port ;
unsigned char b [ sizeof ( uuid_le ) - sizeof ( unsigned int ) ] ;
} ;
} ;
/* Per-socket state (accessed via vsk->trans) */
struct hvsock {
struct vsock_sock * vsk ;
uuid_le vm_srv_id ;
uuid_le host_srv_id ;
struct vmbus_channel * chan ;
struct vmpacket_descriptor * recv_desc ;
/* The length of the payload not delivered to userland yet */
u32 recv_data_len ;
/* The offset of the payload */
u32 recv_data_off ;
/* Have we sent the zero-length packet (FIN)? */
bool fin_sent ;
} ;
/* In the VM, we support Hyper-V Sockets with AF_VSOCK, and the endpoint is
* < cid , port > ( see struct sockaddr_vm ) . Note : cid is not really used here :
* when we write apps to connect to the host , we can only use VMADDR_CID_ANY
* or VMADDR_CID_HOST ( both are equivalent ) as the remote cid , and when we
* write apps to bind ( ) & listen ( ) in the VM , we can only use VMADDR_CID_ANY
* as the local cid .
*
* On the host , Hyper - V Sockets are supported by Winsock AF_HYPERV :
* https : //docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-
* guide / make - integration - service , and the endpoint is < VmID , ServiceId > with
* the below sockaddr :
*
* struct SOCKADDR_HV
* {
* ADDRESS_FAMILY Family ;
* USHORT Reserved ;
* GUID VmId ;
* GUID ServiceId ;
* } ;
* Note : VmID is not used by Linux VM and actually it isn ' t transmitted via
* VMBus , because here it ' s obvious the host and the VM can easily identify
* each other . Though the VmID is useful on the host , especially in the case
* of Windows container , Linux VM doesn ' t need it at all .
*
* To make use of the AF_VSOCK infrastructure in Linux VM , we have to limit
* the available GUID space of SOCKADDR_HV so that we can create a mapping
* between AF_VSOCK port and SOCKADDR_HV Service GUID . The rule of writing
* Hyper - V Sockets apps on the host and in Linux VM is :
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* The only valid Service GUIDs , from the perspectives of both the host and *
* Linux VM , that can be connected by the other end , must conform to this *
* format : < port > - facb - 11e6 - bd58 - 64006 a7986d3 , and the " port " must be in *
* this range [ 0 , 0x7FFFFFFF ] . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* When we write apps on the host to connect ( ) , the GUID ServiceID is used .
* When we write apps in Linux VM to connect ( ) , we only need to specify the
* port and the driver will form the GUID and use that to request the host .
*
* From the perspective of Linux VM :
* 1. the local ephemeral port ( i . e . the local auto - bound port when we call
* connect ( ) without explicit bind ( ) ) is generated by __vsock_bind_stream ( ) ,
* and the range is [ 1024 , 0xFFFFFFFF ) .
* 2. the remote ephemeral port ( i . e . the auto - generated remote port for
* a connect request initiated by the host ' s connect ( ) ) is generated by
* hvs_remote_addr_init ( ) and the range is [ 0x80000000 , 0xFFFFFFFF ) .
*/
# define MAX_LISTEN_PORT ((u32)0x7FFFFFFF)
# define MAX_VM_LISTEN_PORT MAX_LISTEN_PORT
# define MAX_HOST_LISTEN_PORT MAX_LISTEN_PORT
# define MIN_HOST_EPHEMERAL_PORT (MAX_HOST_LISTEN_PORT + 1)
/* 00000000-facb-11e6-bd58-64006a7986d3 */
static const uuid_le srv_id_template =
UUID_LE ( 0x00000000 , 0xfacb , 0x11e6 , 0xbd , 0x58 ,
0x64 , 0x00 , 0x6a , 0x79 , 0x86 , 0xd3 ) ;
static bool is_valid_srv_id ( const uuid_le * id )
{
return ! memcmp ( & id - > b [ 4 ] , & srv_id_template . b [ 4 ] , sizeof ( uuid_le ) - 4 ) ;
}
static unsigned int get_port_by_srv_id ( const uuid_le * svr_id )
{
return * ( ( unsigned int * ) svr_id ) ;
}
static void hvs_addr_init ( struct sockaddr_vm * addr , const uuid_le * svr_id )
{
unsigned int port = get_port_by_srv_id ( svr_id ) ;
vsock_addr_init ( addr , VMADDR_CID_ANY , port ) ;
}
static void hvs_remote_addr_init ( struct sockaddr_vm * remote ,
struct sockaddr_vm * local )
{
static u32 host_ephemeral_port = MIN_HOST_EPHEMERAL_PORT ;
struct sock * sk ;
vsock_addr_init ( remote , VMADDR_CID_ANY , VMADDR_PORT_ANY ) ;
while ( 1 ) {
/* Wrap around ? */
if ( host_ephemeral_port < MIN_HOST_EPHEMERAL_PORT | |
host_ephemeral_port = = VMADDR_PORT_ANY )
host_ephemeral_port = MIN_HOST_EPHEMERAL_PORT ;
remote - > svm_port = host_ephemeral_port + + ;
sk = vsock_find_connected_socket ( remote , local ) ;
if ( ! sk ) {
/* Found an available ephemeral port */
return ;
}
/* Release refcnt got in vsock_find_connected_socket */
sock_put ( sk ) ;
}
}
static void hvs_set_channel_pending_send_size ( struct vmbus_channel * chan )
{
set_channel_pending_send_size ( chan ,
HVS_PKT_LEN ( HVS_SEND_BUF_SIZE ) ) ;
/* See hvs_stream_has_space(): we must make sure the host has seen
* the new pending send size , before we can re - check the writable
* bytes .
*/
virt_mb ( ) ;
}
static void hvs_clear_channel_pending_send_size ( struct vmbus_channel * chan )
{
set_channel_pending_send_size ( chan , 0 ) ;
/* Ditto */
virt_mb ( ) ;
}
static bool hvs_channel_readable ( struct vmbus_channel * chan )
{
u32 readable = hv_get_bytes_to_read ( & chan - > inbound ) ;
/* 0-size payload means FIN */
return readable > = HVS_PKT_LEN ( 0 ) ;
}
static int hvs_channel_readable_payload ( struct vmbus_channel * chan )
{
u32 readable = hv_get_bytes_to_read ( & chan - > inbound ) ;
if ( readable > HVS_PKT_LEN ( 0 ) ) {
/* At least we have 1 byte to read. We don't need to return
* the exact readable bytes : see vsock_stream_recvmsg ( ) - >
* vsock_stream_has_data ( ) .
*/
return 1 ;
}
if ( readable = = HVS_PKT_LEN ( 0 ) ) {
/* 0-size payload means FIN */
return 0 ;
}
/* No payload or FIN */
return - 1 ;
}
static size_t hvs_channel_writable_bytes ( struct vmbus_channel * chan )
{
u32 writeable = hv_get_bytes_to_write ( & chan - > outbound ) ;
size_t ret ;
/* The ringbuffer mustn't be 100% full, and we should reserve a
* zero - length - payload packet for the FIN : see hv_ringbuffer_write ( )
* and hvs_shutdown ( ) .
*/
if ( writeable < = HVS_PKT_LEN ( 1 ) + HVS_PKT_LEN ( 0 ) )
return 0 ;
ret = writeable - HVS_PKT_LEN ( 1 ) - HVS_PKT_LEN ( 0 ) ;
return round_down ( ret , 8 ) ;
}
static int hvs_send_data ( struct vmbus_channel * chan ,
struct hvs_send_buf * send_buf , size_t to_write )
{
send_buf - > hdr . pkt_type = 1 ;
send_buf - > hdr . data_size = to_write ;
return vmbus_sendpacket ( chan , & send_buf - > hdr ,
sizeof ( send_buf - > hdr ) + to_write ,
0 , VM_PKT_DATA_INBAND , 0 ) ;
}
static void hvs_channel_cb ( void * ctx )
{
struct sock * sk = ( struct sock * ) ctx ;
struct vsock_sock * vsk = vsock_sk ( sk ) ;
struct hvsock * hvs = vsk - > trans ;
struct vmbus_channel * chan = hvs - > chan ;
if ( hvs_channel_readable ( chan ) )
sk - > sk_data_ready ( sk ) ;
/* See hvs_stream_has_space(): when we reach here, the writable bytes
* may be already less than HVS_PKT_LEN ( HVS_SEND_BUF_SIZE ) .
*/
if ( hv_get_bytes_to_write ( & chan - > outbound ) > 0 )
sk - > sk_write_space ( sk ) ;
}
static void hvs_close_connection ( struct vmbus_channel * chan )
{
struct sock * sk = get_per_channel_state ( chan ) ;
struct vsock_sock * vsk = vsock_sk ( sk ) ;
2017-10-19 06:33:14 +03:00
lock_sock ( sk ) ;
2017-10-05 23:46:52 +03:00
sk - > sk_state = TCP_CLOSE ;
2017-08-26 07:52:43 +03:00
sock_set_flag ( sk , SOCK_DONE ) ;
vsk - > peer_shutdown | = SEND_SHUTDOWN | RCV_SHUTDOWN ;
sk - > sk_state_change ( sk ) ;
2017-10-19 06:33:14 +03:00
release_sock ( sk ) ;
2017-08-26 07:52:43 +03:00
}
static void hvs_open_connection ( struct vmbus_channel * chan )
{
uuid_le * if_instance , * if_type ;
unsigned char conn_from_host ;
struct sockaddr_vm addr ;
struct sock * sk , * new = NULL ;
struct vsock_sock * vnew ;
struct hvsock * hvs , * hvs_new ;
int ret ;
if_type = & chan - > offermsg . offer . if_type ;
if_instance = & chan - > offermsg . offer . if_instance ;
conn_from_host = chan - > offermsg . offer . u . pipe . user_def [ 0 ] ;
/* The host or the VM should only listen on a port in
* [ 0 , MAX_LISTEN_PORT ]
*/
if ( ! is_valid_srv_id ( if_type ) | |
get_port_by_srv_id ( if_type ) > MAX_LISTEN_PORT )
return ;
hvs_addr_init ( & addr , conn_from_host ? if_type : if_instance ) ;
sk = vsock_find_bound_socket ( & addr ) ;
if ( ! sk )
return ;
2017-10-19 06:33:14 +03:00
lock_sock ( sk ) ;
2017-10-05 23:46:52 +03:00
if ( ( conn_from_host & & sk - > sk_state ! = TCP_LISTEN ) | |
( ! conn_from_host & & sk - > sk_state ! = TCP_SYN_SENT ) )
2017-08-26 07:52:43 +03:00
goto out ;
if ( conn_from_host ) {
if ( sk - > sk_ack_backlog > = sk - > sk_max_ack_backlog )
goto out ;
new = __vsock_create ( sock_net ( sk ) , NULL , sk , GFP_KERNEL ,
sk - > sk_type , 0 ) ;
if ( ! new )
goto out ;
2017-10-05 23:46:52 +03:00
new - > sk_state = TCP_SYN_SENT ;
2017-08-26 07:52:43 +03:00
vnew = vsock_sk ( new ) ;
hvs_new = vnew - > trans ;
hvs_new - > chan = chan ;
} else {
hvs = vsock_sk ( sk ) - > trans ;
hvs - > chan = chan ;
}
set_channel_read_mode ( chan , HV_CALL_DIRECT ) ;
ret = vmbus_open ( chan , RINGBUFFER_HVS_SND_SIZE ,
RINGBUFFER_HVS_RCV_SIZE , NULL , 0 ,
hvs_channel_cb , conn_from_host ? new : sk ) ;
if ( ret ! = 0 ) {
if ( conn_from_host ) {
hvs_new - > chan = NULL ;
sock_put ( new ) ;
} else {
hvs - > chan = NULL ;
}
goto out ;
}
set_per_channel_state ( chan , conn_from_host ? new : sk ) ;
vmbus_set_chn_rescind_callback ( chan , hvs_close_connection ) ;
if ( conn_from_host ) {
2017-10-05 23:46:52 +03:00
new - > sk_state = TCP_ESTABLISHED ;
2017-08-26 07:52:43 +03:00
sk - > sk_ack_backlog + + ;
hvs_addr_init ( & vnew - > local_addr , if_type ) ;
hvs_remote_addr_init ( & vnew - > remote_addr , & vnew - > local_addr ) ;
hvs_new - > vm_srv_id = * if_type ;
hvs_new - > host_srv_id = * if_instance ;
vsock_insert_connected ( vnew ) ;
vsock_enqueue_accept ( sk , new ) ;
} else {
2017-10-05 23:46:52 +03:00
sk - > sk_state = TCP_ESTABLISHED ;
2017-08-26 07:52:43 +03:00
sk - > sk_socket - > state = SS_CONNECTED ;
vsock_insert_connected ( vsock_sk ( sk ) ) ;
}
sk - > sk_state_change ( sk ) ;
out :
/* Release refcnt obtained when we called vsock_find_bound_socket() */
sock_put ( sk ) ;
2017-10-19 06:33:14 +03:00
release_sock ( sk ) ;
2017-08-26 07:52:43 +03:00
}
static u32 hvs_get_local_cid ( void )
{
return VMADDR_CID_ANY ;
}
static int hvs_sock_init ( struct vsock_sock * vsk , struct vsock_sock * psk )
{
struct hvsock * hvs ;
hvs = kzalloc ( sizeof ( * hvs ) , GFP_KERNEL ) ;
if ( ! hvs )
return - ENOMEM ;
vsk - > trans = hvs ;
hvs - > vsk = vsk ;
return 0 ;
}
static int hvs_connect ( struct vsock_sock * vsk )
{
union hvs_service_id vm , host ;
struct hvsock * h = vsk - > trans ;
vm . srv_id = srv_id_template ;
vm . svm_port = vsk - > local_addr . svm_port ;
h - > vm_srv_id = vm . srv_id ;
host . srv_id = srv_id_template ;
host . svm_port = vsk - > remote_addr . svm_port ;
h - > host_srv_id = host . srv_id ;
return vmbus_send_tl_connect_request ( & h - > vm_srv_id , & h - > host_srv_id ) ;
}
static int hvs_shutdown ( struct vsock_sock * vsk , int mode )
{
struct sock * sk = sk_vsock ( vsk ) ;
struct vmpipe_proto_header hdr ;
struct hvs_send_buf * send_buf ;
struct hvsock * hvs ;
if ( ! ( mode & SEND_SHUTDOWN ) )
return 0 ;
lock_sock ( sk ) ;
hvs = vsk - > trans ;
if ( hvs - > fin_sent )
goto out ;
send_buf = ( struct hvs_send_buf * ) & hdr ;
/* It can't fail: see hvs_channel_writable_bytes(). */
( void ) hvs_send_data ( hvs - > chan , send_buf , 0 ) ;
hvs - > fin_sent = true ;
out :
release_sock ( sk ) ;
return 0 ;
}
static void hvs_release ( struct vsock_sock * vsk )
{
2017-10-19 06:33:14 +03:00
struct sock * sk = sk_vsock ( vsk ) ;
2017-08-26 07:52:43 +03:00
struct hvsock * hvs = vsk - > trans ;
2017-10-19 06:33:14 +03:00
struct vmbus_channel * chan ;
2017-08-26 07:52:43 +03:00
2017-10-19 06:33:14 +03:00
lock_sock ( sk ) ;
sk - > sk_state = SS_DISCONNECTING ;
vsock_remove_sock ( vsk ) ;
release_sock ( sk ) ;
2017-08-26 07:52:43 +03:00
2017-10-19 06:33:14 +03:00
chan = hvs - > chan ;
2017-08-26 07:52:43 +03:00
if ( chan )
hvs_shutdown ( vsk , RCV_SHUTDOWN | SEND_SHUTDOWN ) ;
}
static void hvs_destruct ( struct vsock_sock * vsk )
{
struct hvsock * hvs = vsk - > trans ;
struct vmbus_channel * chan = hvs - > chan ;
if ( chan )
vmbus_hvsock_device_unregister ( chan ) ;
kfree ( hvs ) ;
}
static int hvs_dgram_bind ( struct vsock_sock * vsk , struct sockaddr_vm * addr )
{
return - EOPNOTSUPP ;
}
static int hvs_dgram_dequeue ( struct vsock_sock * vsk , struct msghdr * msg ,
size_t len , int flags )
{
return - EOPNOTSUPP ;
}
static int hvs_dgram_enqueue ( struct vsock_sock * vsk ,
struct sockaddr_vm * remote , struct msghdr * msg ,
size_t dgram_len )
{
return - EOPNOTSUPP ;
}
static bool hvs_dgram_allow ( u32 cid , u32 port )
{
return false ;
}
static int hvs_update_recv_data ( struct hvsock * hvs )
{
struct hvs_recv_buf * recv_buf ;
u32 payload_len ;
recv_buf = ( struct hvs_recv_buf * ) ( hvs - > recv_desc + 1 ) ;
payload_len = recv_buf - > hdr . data_size ;
if ( payload_len > HVS_MTU_SIZE )
return - EIO ;
if ( payload_len = = 0 )
hvs - > vsk - > peer_shutdown | = SEND_SHUTDOWN ;
hvs - > recv_data_len = payload_len ;
hvs - > recv_data_off = 0 ;
return 0 ;
}
static ssize_t hvs_stream_dequeue ( struct vsock_sock * vsk , struct msghdr * msg ,
size_t len , int flags )
{
struct hvsock * hvs = vsk - > trans ;
bool need_refill = ! hvs - > recv_desc ;
struct hvs_recv_buf * recv_buf ;
u32 to_read ;
int ret ;
if ( flags & MSG_PEEK )
return - EOPNOTSUPP ;
if ( need_refill ) {
hvs - > recv_desc = hv_pkt_iter_first ( hvs - > chan ) ;
ret = hvs_update_recv_data ( hvs ) ;
if ( ret )
return ret ;
}
recv_buf = ( struct hvs_recv_buf * ) ( hvs - > recv_desc + 1 ) ;
to_read = min_t ( u32 , len , hvs - > recv_data_len ) ;
ret = memcpy_to_msg ( msg , recv_buf - > data + hvs - > recv_data_off , to_read ) ;
if ( ret ! = 0 )
return ret ;
hvs - > recv_data_len - = to_read ;
if ( hvs - > recv_data_len = = 0 ) {
hvs - > recv_desc = hv_pkt_iter_next ( hvs - > chan , hvs - > recv_desc ) ;
if ( hvs - > recv_desc ) {
ret = hvs_update_recv_data ( hvs ) ;
if ( ret )
return ret ;
}
} else {
hvs - > recv_data_off + = to_read ;
}
return to_read ;
}
static ssize_t hvs_stream_enqueue ( struct vsock_sock * vsk , struct msghdr * msg ,
size_t len )
{
struct hvsock * hvs = vsk - > trans ;
struct vmbus_channel * chan = hvs - > chan ;
struct hvs_send_buf * send_buf ;
ssize_t to_write , max_writable , ret ;
BUILD_BUG_ON ( sizeof ( * send_buf ) ! = PAGE_SIZE_4K ) ;
send_buf = kmalloc ( sizeof ( * send_buf ) , GFP_KERNEL ) ;
if ( ! send_buf )
return - ENOMEM ;
max_writable = hvs_channel_writable_bytes ( chan ) ;
to_write = min_t ( ssize_t , len , max_writable ) ;
to_write = min_t ( ssize_t , to_write , HVS_SEND_BUF_SIZE ) ;
ret = memcpy_from_msg ( send_buf - > data , msg , to_write ) ;
if ( ret < 0 )
goto out ;
ret = hvs_send_data ( hvs - > chan , send_buf , to_write ) ;
if ( ret < 0 )
goto out ;
ret = to_write ;
out :
kfree ( send_buf ) ;
return ret ;
}
static s64 hvs_stream_has_data ( struct vsock_sock * vsk )
{
struct hvsock * hvs = vsk - > trans ;
s64 ret ;
if ( hvs - > recv_data_len > 0 )
return 1 ;
switch ( hvs_channel_readable_payload ( hvs - > chan ) ) {
case 1 :
ret = 1 ;
break ;
case 0 :
vsk - > peer_shutdown | = SEND_SHUTDOWN ;
ret = 0 ;
break ;
default : /* -1 */
ret = 0 ;
break ;
}
return ret ;
}
static s64 hvs_stream_has_space ( struct vsock_sock * vsk )
{
struct hvsock * hvs = vsk - > trans ;
struct vmbus_channel * chan = hvs - > chan ;
s64 ret ;
ret = hvs_channel_writable_bytes ( chan ) ;
if ( ret > 0 ) {
hvs_clear_channel_pending_send_size ( chan ) ;
} else {
/* See hvs_channel_cb() */
hvs_set_channel_pending_send_size ( chan ) ;
/* Re-check the writable bytes to avoid race */
ret = hvs_channel_writable_bytes ( chan ) ;
if ( ret > 0 )
hvs_clear_channel_pending_send_size ( chan ) ;
}
return ret ;
}
static u64 hvs_stream_rcvhiwat ( struct vsock_sock * vsk )
{
return HVS_MTU_SIZE + 1 ;
}
static bool hvs_stream_is_active ( struct vsock_sock * vsk )
{
struct hvsock * hvs = vsk - > trans ;
return hvs - > chan ! = NULL ;
}
static bool hvs_stream_allow ( u32 cid , u32 port )
{
/* The host's port range [MIN_HOST_EPHEMERAL_PORT, 0xFFFFFFFF) is
* reserved as ephemeral ports , which are used as the host ' s ports
* when the host initiates connections .
*
* Perform this check in the guest so an immediate error is produced
* instead of a timeout .
*/
if ( port > MAX_HOST_LISTEN_PORT )
return false ;
if ( cid = = VMADDR_CID_HOST )
return true ;
return false ;
}
static
int hvs_notify_poll_in ( struct vsock_sock * vsk , size_t target , bool * readable )
{
struct hvsock * hvs = vsk - > trans ;
* readable = hvs_channel_readable ( hvs - > chan ) ;
return 0 ;
}
static
int hvs_notify_poll_out ( struct vsock_sock * vsk , size_t target , bool * writable )
{
* writable = hvs_stream_has_space ( vsk ) > 0 ;
return 0 ;
}
static
int hvs_notify_recv_init ( struct vsock_sock * vsk , size_t target ,
struct vsock_transport_recv_notify_data * d )
{
return 0 ;
}
static
int hvs_notify_recv_pre_block ( struct vsock_sock * vsk , size_t target ,
struct vsock_transport_recv_notify_data * d )
{
return 0 ;
}
static
int hvs_notify_recv_pre_dequeue ( struct vsock_sock * vsk , size_t target ,
struct vsock_transport_recv_notify_data * d )
{
return 0 ;
}
static
int hvs_notify_recv_post_dequeue ( struct vsock_sock * vsk , size_t target ,
ssize_t copied , bool data_read ,
struct vsock_transport_recv_notify_data * d )
{
return 0 ;
}
static
int hvs_notify_send_init ( struct vsock_sock * vsk ,
struct vsock_transport_send_notify_data * d )
{
return 0 ;
}
static
int hvs_notify_send_pre_block ( struct vsock_sock * vsk ,
struct vsock_transport_send_notify_data * d )
{
return 0 ;
}
static
int hvs_notify_send_pre_enqueue ( struct vsock_sock * vsk ,
struct vsock_transport_send_notify_data * d )
{
return 0 ;
}
static
int hvs_notify_send_post_enqueue ( struct vsock_sock * vsk , ssize_t written ,
struct vsock_transport_send_notify_data * d )
{
return 0 ;
}
static void hvs_set_buffer_size ( struct vsock_sock * vsk , u64 val )
{
/* Ignored. */
}
static void hvs_set_min_buffer_size ( struct vsock_sock * vsk , u64 val )
{
/* Ignored. */
}
static void hvs_set_max_buffer_size ( struct vsock_sock * vsk , u64 val )
{
/* Ignored. */
}
static u64 hvs_get_buffer_size ( struct vsock_sock * vsk )
{
return - ENOPROTOOPT ;
}
static u64 hvs_get_min_buffer_size ( struct vsock_sock * vsk )
{
return - ENOPROTOOPT ;
}
static u64 hvs_get_max_buffer_size ( struct vsock_sock * vsk )
{
return - ENOPROTOOPT ;
}
static struct vsock_transport hvs_transport = {
. get_local_cid = hvs_get_local_cid ,
. init = hvs_sock_init ,
. destruct = hvs_destruct ,
. release = hvs_release ,
. connect = hvs_connect ,
. shutdown = hvs_shutdown ,
. dgram_bind = hvs_dgram_bind ,
. dgram_dequeue = hvs_dgram_dequeue ,
. dgram_enqueue = hvs_dgram_enqueue ,
. dgram_allow = hvs_dgram_allow ,
. stream_dequeue = hvs_stream_dequeue ,
. stream_enqueue = hvs_stream_enqueue ,
. stream_has_data = hvs_stream_has_data ,
. stream_has_space = hvs_stream_has_space ,
. stream_rcvhiwat = hvs_stream_rcvhiwat ,
. stream_is_active = hvs_stream_is_active ,
. stream_allow = hvs_stream_allow ,
. notify_poll_in = hvs_notify_poll_in ,
. notify_poll_out = hvs_notify_poll_out ,
. notify_recv_init = hvs_notify_recv_init ,
. notify_recv_pre_block = hvs_notify_recv_pre_block ,
. notify_recv_pre_dequeue = hvs_notify_recv_pre_dequeue ,
. notify_recv_post_dequeue = hvs_notify_recv_post_dequeue ,
. notify_send_init = hvs_notify_send_init ,
. notify_send_pre_block = hvs_notify_send_pre_block ,
. notify_send_pre_enqueue = hvs_notify_send_pre_enqueue ,
. notify_send_post_enqueue = hvs_notify_send_post_enqueue ,
. set_buffer_size = hvs_set_buffer_size ,
. set_min_buffer_size = hvs_set_min_buffer_size ,
. set_max_buffer_size = hvs_set_max_buffer_size ,
. get_buffer_size = hvs_get_buffer_size ,
. get_min_buffer_size = hvs_get_min_buffer_size ,
. get_max_buffer_size = hvs_get_max_buffer_size ,
} ;
static int hvs_probe ( struct hv_device * hdev ,
const struct hv_vmbus_device_id * dev_id )
{
struct vmbus_channel * chan = hdev - > channel ;
hvs_open_connection ( chan ) ;
/* Always return success to suppress the unnecessary error message
* in vmbus_probe ( ) : on error the host will rescind the device in
* 30 seconds and we can do cleanup at that time in
* vmbus_onoffer_rescind ( ) .
*/
return 0 ;
}
static int hvs_remove ( struct hv_device * hdev )
{
struct vmbus_channel * chan = hdev - > channel ;
vmbus_close ( chan ) ;
return 0 ;
}
/* This isn't really used. See vmbus_match() and vmbus_probe() */
static const struct hv_vmbus_device_id id_table [ ] = {
{ } ,
} ;
static struct hv_driver hvs_drv = {
. name = " hv_sock " ,
. hvsock = true ,
. id_table = id_table ,
. probe = hvs_probe ,
. remove = hvs_remove ,
} ;
static int __init hvs_init ( void )
{
int ret ;
if ( vmbus_proto_version < VERSION_WIN10 )
return - ENODEV ;
ret = vmbus_driver_register ( & hvs_drv ) ;
if ( ret ! = 0 )
return ret ;
ret = vsock_core_init ( & hvs_transport ) ;
if ( ret ) {
vmbus_driver_unregister ( & hvs_drv ) ;
return ret ;
}
return 0 ;
}
static void __exit hvs_exit ( void )
{
vsock_core_exit ( ) ;
vmbus_driver_unregister ( & hvs_drv ) ;
}
module_init ( hvs_init ) ;
module_exit ( hvs_exit ) ;
MODULE_DESCRIPTION ( " Hyper-V Sockets " ) ;
MODULE_VERSION ( " 1.0.0 " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_NETPROTO ( PF_VSOCK ) ;