2014-02-16 23:34:30 +04:00
/*
* An implementation of file copy service .
*
* Copyright ( C ) 2014 , Microsoft , Inc .
*
* Author : K . Y . Srinivasan < ksrinivasan @ novell . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/nls.h>
# include <linux/workqueue.h>
# include <linux/hyperv.h>
# include <linux/sched.h>
# include "hyperv_vmbus.h"
2015-04-12 04:07:53 +03:00
# include "hv_utils_transport.h"
2014-02-16 23:34:30 +04:00
# define WIN8_SRV_MAJOR 1
# define WIN8_SRV_MINOR 1
# define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
2017-01-28 22:37:17 +03:00
# define FCOPY_VER_COUNT 1
static const int fcopy_versions [ ] = {
WIN8_SRV_VERSION
} ;
# define FW_VER_COUNT 1
static const int fw_versions [ ] = {
UTIL_FW_VERSION
} ;
2014-02-16 23:34:30 +04:00
/*
* Global state maintained for transaction that is being processed .
* For a class of integration services , including the " file copy service " ,
* the specified protocol is a " request/response " protocol which means that
* there can only be single outstanding transaction from the host at any
* given point in time . We use this to simplify memory management in this
* driver - we cache and process only one message at a time .
*
* While the request / response protocol is guaranteed by the host , we further
* ensure this by serializing packet processing in this driver - we do not
* read additional packets from the VMBUs until the current packet is fully
* handled .
*/
static struct {
2015-04-12 04:07:49 +03:00
int state ; /* hvutil_device_state */
2014-02-16 23:34:30 +04:00
int recv_len ; /* number of bytes received. */
struct hv_fcopy_hdr * fcopy_msg ; /* current message */
struct vmbus_channel * recv_channel ; /* chn we got the request */
u64 recv_req_id ; /* request ID. */
} fcopy_transaction ;
static void fcopy_respond_to_host ( int error ) ;
2015-04-12 04:07:53 +03:00
static void fcopy_send_data ( struct work_struct * dummy ) ;
2015-04-12 04:07:45 +03:00
static void fcopy_timeout_func ( struct work_struct * dummy ) ;
static DECLARE_DELAYED_WORK ( fcopy_timeout_work , fcopy_timeout_func ) ;
2015-04-12 04:07:53 +03:00
static DECLARE_WORK ( fcopy_send_work , fcopy_send_data ) ;
static const char fcopy_devname [ ] = " vmbus/hv_fcopy " ;
2014-02-16 23:34:30 +04:00
static u8 * recv_buffer ;
2015-04-12 04:07:53 +03:00
static struct hvutil_transport * hvt ;
2015-04-12 04:07:58 +03:00
/*
* This state maintains the version number registered by the daemon .
*/
static int dm_reg_value ;
2014-02-16 23:34:30 +04:00
2015-12-15 03:01:33 +03:00
static void fcopy_poll_wrapper ( void * channel )
{
/* Transaction is finished, reset the state here to avoid races. */
fcopy_transaction . state = HVUTIL_READY ;
hv_fcopy_onchannelcallback ( channel ) ;
}
2015-04-12 04:07:45 +03:00
static void fcopy_timeout_func ( struct work_struct * dummy )
2014-02-16 23:34:30 +04:00
{
/*
* If the timer fires , the user - mode component has not responded ;
* process the pending transaction .
*/
fcopy_respond_to_host ( HV_E_FAIL ) ;
2015-12-15 03:01:33 +03:00
hv_poll_channel ( fcopy_transaction . recv_channel , fcopy_poll_wrapper ) ;
2014-02-16 23:34:30 +04:00
}
2016-06-10 03:08:57 +03:00
static void fcopy_register_done ( void )
{
pr_debug ( " FCP: userspace daemon registered \n " ) ;
hv_poll_channel ( fcopy_transaction . recv_channel , fcopy_poll_wrapper ) ;
}
2014-02-16 23:34:30 +04:00
static int fcopy_handle_handshake ( u32 version )
{
2015-04-12 04:07:58 +03:00
u32 our_ver = FCOPY_CURRENT_VERSION ;
2014-02-16 23:34:30 +04:00
switch ( version ) {
2015-04-12 04:07:58 +03:00
case FCOPY_VERSION_0 :
/* Daemon doesn't expect us to reply */
dm_reg_value = version ;
break ;
case FCOPY_VERSION_1 :
/* Daemon expects us to reply with our own version */
2016-06-10 03:08:57 +03:00
if ( hvutil_transport_send ( hvt , & our_ver , sizeof ( our_ver ) ,
fcopy_register_done ) )
2015-04-12 04:07:58 +03:00
return - EFAULT ;
dm_reg_value = version ;
2014-02-16 23:34:30 +04:00
break ;
default :
/*
* For now we will fail the registration .
* If and when we have multiple versions to
* deal with , we will be backward compatible .
* We will add this code when needed .
*/
return - EINVAL ;
}
2016-06-10 03:08:57 +03:00
pr_debug ( " FCP: userspace daemon ver. %d connected \n " , version ) ;
2014-02-16 23:34:30 +04:00
return 0 ;
}
2015-04-12 04:07:53 +03:00
static void fcopy_send_data ( struct work_struct * dummy )
2014-02-16 23:34:30 +04:00
{
2015-08-02 02:08:12 +03:00
struct hv_start_fcopy * smsg_out = NULL ;
2014-02-16 23:34:30 +04:00
int operation = fcopy_transaction . fcopy_msg - > operation ;
struct hv_start_fcopy * smsg_in ;
2015-04-12 04:07:53 +03:00
void * out_src ;
int rc , out_len ;
2014-02-16 23:34:30 +04:00
/*
* The strings sent from the host are encoded in
* in utf16 ; convert it to utf8 strings .
* The host assures us that the utf16 strings will not exceed
* the max lengths specified . We will however , reserve room
* for the string terminating character - in the utf16s_utf8s ( )
* function we limit the size of the buffer where the converted
* string is placed to W_MAX_PATH - 1 to guarantee
* that the strings can be properly terminated !
*/
switch ( operation ) {
case START_FILE_COPY :
2015-04-12 04:07:53 +03:00
out_len = sizeof ( struct hv_start_fcopy ) ;
2015-08-02 02:08:12 +03:00
smsg_out = kzalloc ( sizeof ( * smsg_out ) , GFP_KERNEL ) ;
if ( ! smsg_out )
return ;
smsg_out - > hdr . operation = operation ;
2014-02-16 23:34:30 +04:00
smsg_in = ( struct hv_start_fcopy * ) fcopy_transaction . fcopy_msg ;
utf16s_to_utf8s ( ( wchar_t * ) smsg_in - > file_name , W_MAX_PATH ,
UTF16_LITTLE_ENDIAN ,
2015-08-02 02:08:12 +03:00
( __u8 * ) & smsg_out - > file_name , W_MAX_PATH - 1 ) ;
2014-02-16 23:34:30 +04:00
utf16s_to_utf8s ( ( wchar_t * ) smsg_in - > path_name , W_MAX_PATH ,
UTF16_LITTLE_ENDIAN ,
2015-08-02 02:08:12 +03:00
( __u8 * ) & smsg_out - > path_name , W_MAX_PATH - 1 ) ;
2014-02-16 23:34:30 +04:00
2015-08-02 02:08:12 +03:00
smsg_out - > copy_flags = smsg_in - > copy_flags ;
smsg_out - > file_size = smsg_in - > file_size ;
out_src = smsg_out ;
2014-02-16 23:34:30 +04:00
break ;
default :
2015-04-12 04:07:53 +03:00
out_src = fcopy_transaction . fcopy_msg ;
out_len = fcopy_transaction . recv_len ;
2014-02-16 23:34:30 +04:00
break ;
}
2015-04-12 04:07:53 +03:00
fcopy_transaction . state = HVUTIL_USERSPACE_REQ ;
2016-06-10 03:08:57 +03:00
rc = hvutil_transport_send ( hvt , out_src , out_len , NULL ) ;
2015-04-12 04:07:53 +03:00
if ( rc ) {
pr_debug ( " FCP: failed to communicate to the daemon: %d \n " , rc ) ;
if ( cancel_delayed_work_sync ( & fcopy_timeout_work ) ) {
fcopy_respond_to_host ( HV_E_FAIL ) ;
fcopy_transaction . state = HVUTIL_READY ;
}
}
2015-08-02 02:08:12 +03:00
kfree ( smsg_out ) ;
2014-02-16 23:34:30 +04:00
}
/*
* Send a response back to the host .
*/
static void
fcopy_respond_to_host ( int error )
{
struct icmsg_hdr * icmsghdr ;
u32 buf_len ;
struct vmbus_channel * channel ;
u64 req_id ;
/*
* Copy the global state for completing the transaction . Note that
* only one transaction can be active at a time . This is guaranteed
* by the file copy protocol implemented by the host . Furthermore ,
* the " transaction active " state we maintain ensures that there can
* only be one active transaction at a time .
*/
buf_len = fcopy_transaction . recv_len ;
channel = fcopy_transaction . recv_channel ;
req_id = fcopy_transaction . recv_req_id ;
icmsghdr = ( struct icmsg_hdr * )
& recv_buffer [ sizeof ( struct vmbuspipe_hdr ) ] ;
if ( channel - > onchannel_callback = = NULL )
/*
* We have raced with util driver being unloaded ;
* silently return .
*/
return ;
icmsghdr - > status = error ;
icmsghdr - > icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE ;
vmbus_sendpacket ( channel , recv_buffer , buf_len , req_id ,
VM_PKT_DATA_INBAND , 0 ) ;
}
void hv_fcopy_onchannelcallback ( void * context )
{
struct vmbus_channel * channel = context ;
u32 recvlen ;
u64 requestid ;
struct hv_fcopy_hdr * fcopy_msg ;
struct icmsg_hdr * icmsghdr ;
int fcopy_srv_version ;
2015-12-15 03:01:33 +03:00
if ( fcopy_transaction . state > HVUTIL_READY )
2014-02-16 23:34:30 +04:00
return ;
vmbus_recvpacket ( channel , recv_buffer , PAGE_SIZE * 2 , & recvlen ,
& requestid ) ;
if ( recvlen < = 0 )
return ;
icmsghdr = ( struct icmsg_hdr * ) & recv_buffer [
sizeof ( struct vmbuspipe_hdr ) ] ;
if ( icmsghdr - > icmsgtype = = ICMSGTYPE_NEGOTIATE ) {
2017-01-28 22:37:18 +03:00
if ( vmbus_prep_negotiate_resp ( icmsghdr , recv_buffer ,
2017-01-28 22:37:17 +03:00
fw_versions , FW_VER_COUNT ,
fcopy_versions , FCOPY_VER_COUNT ,
2017-01-28 22:37:18 +03:00
NULL , & fcopy_srv_version ) ) {
pr_info ( " FCopy IC version %d.%d \n " ,
fcopy_srv_version > > 16 ,
fcopy_srv_version & 0xFFFF ) ;
}
2014-02-16 23:34:30 +04:00
} else {
fcopy_msg = ( struct hv_fcopy_hdr * ) & recv_buffer [
sizeof ( struct vmbuspipe_hdr ) +
sizeof ( struct icmsg_hdr ) ] ;
/*
* Stash away this global state for completing the
* transaction ; note transactions are serialized .
*/
fcopy_transaction . recv_len = recvlen ;
fcopy_transaction . recv_req_id = requestid ;
fcopy_transaction . fcopy_msg = fcopy_msg ;
2015-04-12 04:07:49 +03:00
if ( fcopy_transaction . state < HVUTIL_READY ) {
/* Userspace is not registered yet */
fcopy_respond_to_host ( HV_E_FAIL ) ;
return ;
}
fcopy_transaction . state = HVUTIL_HOSTMSG_RECEIVED ;
2014-02-16 23:34:30 +04:00
/*
* Send the information to the user - level daemon .
*/
2015-04-12 04:07:53 +03:00
schedule_work ( & fcopy_send_work ) ;
2015-12-15 03:01:32 +03:00
schedule_delayed_work ( & fcopy_timeout_work ,
HV_UTIL_TIMEOUT * HZ ) ;
2014-02-16 23:34:30 +04:00
return ;
}
icmsghdr - > icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE ;
vmbus_sendpacket ( channel , recv_buffer , recvlen , requestid ,
VM_PKT_DATA_INBAND , 0 ) ;
}
2015-04-12 04:07:53 +03:00
/* Callback when data is received from userspace */
static int fcopy_on_msg ( void * msg , int len )
2014-02-16 23:34:30 +04:00
{
2015-04-12 04:07:53 +03:00
int * val = ( int * ) msg ;
2014-02-16 23:34:30 +04:00
2015-04-12 04:07:53 +03:00
if ( len ! = sizeof ( int ) )
2014-02-16 23:34:30 +04:00
return - EINVAL ;
2015-04-12 04:07:53 +03:00
if ( fcopy_transaction . state = = HVUTIL_DEVICE_INIT )
return fcopy_handle_handshake ( * val ) ;
2014-02-16 23:34:30 +04:00
2015-04-12 04:07:49 +03:00
if ( fcopy_transaction . state ! = HVUTIL_USERSPACE_REQ )
return - EINVAL ;
2014-02-16 23:34:30 +04:00
/*
* Complete the transaction by forwarding the result
* to the host . But first , cancel the timeout .
*/
2015-04-12 04:07:49 +03:00
if ( cancel_delayed_work_sync ( & fcopy_timeout_work ) ) {
fcopy_transaction . state = HVUTIL_USERSPACE_RECV ;
2015-04-12 04:07:53 +03:00
fcopy_respond_to_host ( * val ) ;
2015-12-15 03:01:33 +03:00
hv_poll_channel ( fcopy_transaction . recv_channel ,
fcopy_poll_wrapper ) ;
2015-04-12 04:07:42 +03:00
}
2014-02-16 23:34:30 +04:00
return 0 ;
}
2015-04-12 04:07:53 +03:00
static void fcopy_on_reset ( void )
2014-02-16 23:34:30 +04:00
{
/*
* The daemon has exited ; reset the state .
*/
2015-04-12 04:07:49 +03:00
fcopy_transaction . state = HVUTIL_DEVICE_INIT ;
2015-01-14 12:55:10 +03:00
2015-04-12 04:07:53 +03:00
if ( cancel_delayed_work_sync ( & fcopy_timeout_work ) )
2015-01-14 12:55:10 +03:00
fcopy_respond_to_host ( HV_E_FAIL ) ;
2014-02-16 23:34:30 +04:00
}
int hv_fcopy_init ( struct hv_util_service * srv )
{
recv_buffer = srv - > recv_buffer ;
2016-02-27 02:13:19 +03:00
fcopy_transaction . recv_channel = srv - > channel ;
2014-02-16 23:34:30 +04:00
/*
* When this driver loads , the user level daemon that
* processes the host requests may not yet be running .
* Defer processing channel callbacks until the daemon
* has registered .
*/
2015-04-12 04:07:49 +03:00
fcopy_transaction . state = HVUTIL_DEVICE_INIT ;
2014-02-16 23:34:30 +04:00
2015-04-12 04:07:53 +03:00
hvt = hvutil_transport_init ( fcopy_devname , 0 , 0 ,
fcopy_on_msg , fcopy_on_reset ) ;
if ( ! hvt )
return - EFAULT ;
return 0 ;
2014-02-16 23:34:30 +04:00
}
void hv_fcopy_deinit ( void )
{
2015-04-12 04:07:49 +03:00
fcopy_transaction . state = HVUTIL_DEVICE_DYING ;
2015-04-12 04:07:45 +03:00
cancel_delayed_work_sync ( & fcopy_timeout_work ) ;
2015-04-12 04:07:53 +03:00
hvutil_transport_destroy ( hvt ) ;
2014-02-16 23:34:30 +04:00
}