2013-03-15 23:30:06 +04:00
/*
* An implementation of host initiated guest snapshot .
*
*
* Copyright ( C ) 2013 , Microsoft , Inc .
* Author : K . Y . Srinivasan < kys @ microsoft . 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/net.h>
# include <linux/nls.h>
# include <linux/connector.h>
# include <linux/workqueue.h>
# include <linux/hyperv.h>
2015-04-12 04:07:39 +03:00
# include "hyperv_vmbus.h"
2015-04-12 04:07:52 +03:00
# include "hv_utils_transport.h"
2015-04-12 04:07:39 +03:00
2013-07-02 21:31:30 +04:00
# define VSS_MAJOR 5
# define VSS_MINOR 0
2013-09-06 22:49:56 +04:00
# define VSS_VERSION (VSS_MAJOR << 16 | VSS_MINOR)
2013-07-02 21:31:30 +04:00
2017-01-28 22:37:17 +03:00
# define VSS_VER_COUNT 1
static const int vss_versions [ ] = {
VSS_VERSION
} ;
# define FW_VER_COUNT 1
static const int fw_versions [ ] = {
UTIL_FW_VERSION
} ;
2016-11-07 00:14:11 +03:00
/*
* Timeout values are based on expecations from host
*/
# define VSS_FREEZE_TIMEOUT (15 * 60)
2013-03-15 23:30:06 +04:00
/*
2015-04-12 04:07:48 +03:00
* Global state maintained for transaction that is being processed . For a class
* of integration services , including the " VSS 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 .
2013-03-15 23:30:06 +04:00
*
2015-04-12 04:07:48 +03:00
* 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 .
2013-03-15 23:30:06 +04:00
*/
static struct {
2015-04-12 04:07:48 +03:00
int state ; /* hvutil_device_state */
2013-03-15 23:30:06 +04:00
int recv_len ; /* number of bytes received. */
struct vmbus_channel * recv_channel ; /* chn we got the request */
u64 recv_req_id ; /* request ID. */
struct hv_vss_msg * msg ; /* current message */
} vss_transaction ;
static void vss_respond_to_host ( int error ) ;
2015-04-12 04:07:57 +03:00
/*
* This state maintains the version number registered by the daemon .
*/
static int dm_reg_value ;
2015-04-12 04:07:52 +03:00
static const char vss_devname [ ] = " vmbus/hv_vss " ;
2013-03-15 23:30:06 +04:00
static __u8 * recv_buffer ;
2015-04-12 04:07:52 +03:00
static struct hvutil_transport * hvt ;
2013-03-15 23:30:06 +04:00
2014-11-06 20:21:24 +03:00
static void vss_timeout_func ( struct work_struct * dummy ) ;
2016-09-02 15:58:24 +03:00
static void vss_handle_request ( struct work_struct * dummy ) ;
2014-11-06 20:21:24 +03:00
static DECLARE_DELAYED_WORK ( vss_timeout_work , vss_timeout_func ) ;
2016-09-02 15:58:24 +03:00
static DECLARE_WORK ( vss_handle_request_work , vss_handle_request ) ;
2013-03-15 23:30:06 +04:00
2015-12-15 03:01:33 +03:00
static void vss_poll_wrapper ( void * channel )
{
/* Transaction is finished, reset the state here to avoid races. */
vss_transaction . state = HVUTIL_READY ;
hv_vss_onchannelcallback ( channel ) ;
}
2013-03-15 23:30:06 +04:00
/*
* Callback when data is received from user mode .
*/
2014-11-06 20:21:24 +03:00
static void vss_timeout_func ( struct work_struct * dummy )
{
/*
* Timeout waiting for userspace component to reply happened .
*/
pr_warn ( " VSS: timeout waiting for daemon to reply \n " ) ;
vss_respond_to_host ( HV_E_FAIL ) ;
2015-04-12 04:07:43 +03:00
2015-12-15 03:01:33 +03:00
hv_poll_channel ( vss_transaction . recv_channel , vss_poll_wrapper ) ;
2014-11-06 20:21:24 +03:00
}
2016-06-10 03:08:57 +03:00
static void vss_register_done ( void )
{
hv_poll_channel ( vss_transaction . recv_channel , vss_poll_wrapper ) ;
pr_debug ( " VSS: userspace daemon registered \n " ) ;
}
2015-04-12 04:07:57 +03:00
static int vss_handle_handshake ( struct hv_vss_msg * vss_msg )
{
u32 our_ver = VSS_OP_REGISTER1 ;
switch ( vss_msg - > vss_hdr . operation ) {
case VSS_OP_REGISTER :
/* Daemon doesn't expect us to reply */
dm_reg_value = VSS_OP_REGISTER ;
break ;
case VSS_OP_REGISTER1 :
2016-06-10 03:08:57 +03:00
/* Daemon expects us to reply with our own version */
if ( hvutil_transport_send ( hvt , & our_ver , sizeof ( our_ver ) ,
vss_register_done ) )
2015-04-12 04:07:57 +03:00
return - EFAULT ;
dm_reg_value = VSS_OP_REGISTER1 ;
break ;
default :
return - EINVAL ;
}
2016-11-07 00:14:10 +03:00
pr_info ( " VSS: userspace daemon ver. %d connected \n " , dm_reg_value ) ;
2015-04-12 04:07:57 +03:00
return 0 ;
}
2015-04-12 04:07:52 +03:00
static int vss_on_msg ( void * msg , int len )
2013-03-15 23:30:06 +04:00
{
2015-04-12 04:07:52 +03:00
struct hv_vss_msg * vss_msg = ( struct hv_vss_msg * ) msg ;
2013-03-15 23:30:06 +04:00
2016-11-07 00:14:10 +03:00
if ( len ! = sizeof ( * vss_msg ) ) {
pr_debug ( " VSS: Message size does not match length \n " ) ;
2015-04-12 04:07:52 +03:00
return - EINVAL ;
2016-11-07 00:14:10 +03:00
}
2013-03-15 23:30:06 +04:00
2015-04-12 04:07:57 +03:00
if ( vss_msg - > vss_hdr . operation = = VSS_OP_REGISTER | |
vss_msg - > vss_hdr . operation = = VSS_OP_REGISTER1 ) {
/*
* Don ' t process registration messages if we ' re in the middle
* of a transaction processing .
*/
2016-11-07 00:14:10 +03:00
if ( vss_transaction . state > HVUTIL_READY ) {
pr_debug ( " VSS: Got unexpected registration request \n " ) ;
2015-04-12 04:07:57 +03:00
return - EINVAL ;
2016-11-07 00:14:10 +03:00
}
2015-04-12 04:07:57 +03:00
return vss_handle_handshake ( vss_msg ) ;
2015-04-12 04:07:48 +03:00
} else if ( vss_transaction . state = = HVUTIL_USERSPACE_REQ ) {
vss_transaction . state = HVUTIL_USERSPACE_RECV ;
2016-09-02 15:58:25 +03:00
if ( vss_msg - > vss_hdr . operation = = VSS_OP_HOT_BACKUP )
vss_transaction . msg - > vss_cf . flags =
VSS_HBU_NO_AUTO_RECOVERY ;
2015-04-12 04:07:48 +03:00
if ( cancel_delayed_work_sync ( & vss_timeout_work ) ) {
vss_respond_to_host ( vss_msg - > error ) ;
/* Transaction is finished, reset the state. */
2015-12-15 03:01:33 +03:00
hv_poll_channel ( vss_transaction . recv_channel ,
vss_poll_wrapper ) ;
2015-04-12 04:07:48 +03:00
}
} else {
/* This is a spurious call! */
2016-11-07 00:14:10 +03:00
pr_debug ( " VSS: Transaction not active \n " ) ;
2015-04-12 04:07:52 +03:00
return - EINVAL ;
2013-03-15 23:30:06 +04:00
}
2015-04-12 04:07:52 +03:00
return 0 ;
2013-03-15 23:30:06 +04:00
}
2016-09-02 15:58:24 +03:00
static void vss_send_op ( void )
2013-03-15 23:30:06 +04:00
{
int op = vss_transaction . msg - > vss_hdr . operation ;
2014-11-06 20:21:25 +03:00
int rc ;
2013-03-15 23:30:06 +04:00
struct hv_vss_msg * vss_msg ;
2015-04-12 04:07:48 +03:00
/* The transaction state is wrong. */
2016-11-07 00:14:10 +03:00
if ( vss_transaction . state ! = HVUTIL_HOSTMSG_RECEIVED ) {
pr_debug ( " VSS: Unexpected attempt to send to daemon \n " ) ;
2015-04-12 04:07:48 +03:00
return ;
2016-11-07 00:14:10 +03:00
}
2015-04-12 04:07:48 +03:00
2015-04-12 04:07:52 +03:00
vss_msg = kzalloc ( sizeof ( * vss_msg ) , GFP_KERNEL ) ;
if ( ! vss_msg )
2013-03-15 23:30:06 +04:00
return ;
vss_msg - > vss_hdr . operation = op ;
2015-04-12 04:07:48 +03:00
vss_transaction . state = HVUTIL_USERSPACE_REQ ;
2016-09-02 15:58:24 +03:00
2016-11-07 00:14:11 +03:00
schedule_delayed_work ( & vss_timeout_work , op = = VSS_OP_FREEZE ?
VSS_FREEZE_TIMEOUT * HZ : HV_UTIL_TIMEOUT * HZ ) ;
2016-09-02 15:58:24 +03:00
2016-06-10 03:08:57 +03:00
rc = hvutil_transport_send ( hvt , vss_msg , sizeof ( * vss_msg ) , NULL ) ;
2014-11-06 20:21:25 +03:00
if ( rc ) {
pr_warn ( " VSS: failed to communicate to the daemon: %d \n " , rc ) ;
2015-04-12 04:07:48 +03:00
if ( cancel_delayed_work_sync ( & vss_timeout_work ) ) {
2014-11-06 20:21:25 +03:00
vss_respond_to_host ( HV_E_FAIL ) ;
2015-04-12 04:07:48 +03:00
vss_transaction . state = HVUTIL_READY ;
}
2014-11-06 20:21:25 +03:00
}
2015-04-12 04:07:48 +03:00
2015-04-12 04:07:52 +03:00
kfree ( vss_msg ) ;
2013-03-15 23:30:06 +04:00
}
2016-09-02 15:58:24 +03:00
static void vss_handle_request ( struct work_struct * dummy )
{
switch ( vss_transaction . msg - > vss_hdr . operation ) {
/*
* Initiate a " freeze/thaw " operation in the guest .
* We respond to the host once the operation is complete .
*
* We send the message to the user space daemon and the operation is
* performed in the daemon .
*/
case VSS_OP_THAW :
case VSS_OP_FREEZE :
2016-09-02 15:58:25 +03:00
case VSS_OP_HOT_BACKUP :
2016-09-02 15:58:24 +03:00
if ( vss_transaction . state < HVUTIL_READY ) {
/* Userspace is not registered yet */
2016-11-07 00:14:10 +03:00
pr_debug ( " VSS: Not ready for request. \n " ) ;
2016-09-02 15:58:24 +03:00
vss_respond_to_host ( HV_E_FAIL ) ;
return ;
}
2016-11-07 00:14:10 +03:00
pr_debug ( " VSS: Received request for op code: %d \n " ,
vss_transaction . msg - > vss_hdr . operation ) ;
2016-09-02 15:58:24 +03:00
vss_transaction . state = HVUTIL_HOSTMSG_RECEIVED ;
vss_send_op ( ) ;
return ;
case VSS_OP_GET_DM_INFO :
vss_transaction . msg - > dm_info . flags = 0 ;
break ;
default :
break ;
}
vss_respond_to_host ( 0 ) ;
hv_poll_channel ( vss_transaction . recv_channel , vss_poll_wrapper ) ;
}
2013-03-15 23:30:06 +04:00
/*
* Send a response back to the host .
*/
static void
vss_respond_to_host ( int error )
{
struct icmsg_hdr * icmsghdrp ;
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 .
*/
buf_len = vss_transaction . recv_len ;
channel = vss_transaction . recv_channel ;
req_id = vss_transaction . recv_req_id ;
icmsghdrp = ( 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 ;
icmsghdrp - > status = error ;
icmsghdrp - > icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE ;
vmbus_sendpacket ( channel , recv_buffer , buf_len , req_id ,
VM_PKT_DATA_INBAND , 0 ) ;
}
/*
* This callback is invoked when we get a VSS message from the host .
* The host ensures that only one VSS transaction can be active at a time .
*/
void hv_vss_onchannelcallback ( void * context )
{
struct vmbus_channel * channel = context ;
u32 recvlen ;
u64 requestid ;
struct hv_vss_msg * vss_msg ;
2017-01-28 22:37:18 +03:00
int vss_srv_version ;
2013-03-15 23:30:06 +04:00
struct icmsg_hdr * icmsghdrp ;
2015-12-15 03:01:33 +03:00
if ( vss_transaction . state > HVUTIL_READY )
2013-03-15 23:30:06 +04:00
return ;
vmbus_recvpacket ( channel , recv_buffer , PAGE_SIZE * 2 , & recvlen ,
& requestid ) ;
if ( recvlen > 0 ) {
icmsghdrp = ( struct icmsg_hdr * ) & recv_buffer [
sizeof ( struct vmbuspipe_hdr ) ] ;
if ( icmsghdrp - > icmsgtype = = ICMSGTYPE_NEGOTIATE ) {
2017-01-28 22:37:18 +03:00
if ( vmbus_prep_negotiate_resp ( icmsghdrp ,
2017-01-28 22:37:17 +03:00
recv_buffer , fw_versions , FW_VER_COUNT ,
vss_versions , VSS_VER_COUNT ,
2017-01-28 22:37:18 +03:00
NULL , & vss_srv_version ) ) {
pr_info ( " VSS IC version %d.%d \n " ,
vss_srv_version > > 16 ,
vss_srv_version & 0xFFFF ) ;
}
2013-03-15 23:30:06 +04:00
} else {
vss_msg = ( struct hv_vss_msg * ) & recv_buffer [
sizeof ( struct vmbuspipe_hdr ) +
sizeof ( struct icmsg_hdr ) ] ;
/*
* Stash away this global state for completing the
* transaction ; note transactions are serialized .
*/
vss_transaction . recv_len = recvlen ;
vss_transaction . recv_req_id = requestid ;
vss_transaction . msg = ( struct hv_vss_msg * ) vss_msg ;
2016-09-02 15:58:24 +03:00
schedule_work ( & vss_handle_request_work ) ;
return ;
2013-03-15 23:30:06 +04:00
}
icmsghdrp - > icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE ;
vmbus_sendpacket ( channel , recv_buffer ,
recvlen , requestid ,
VM_PKT_DATA_INBAND , 0 ) ;
}
}
2015-04-12 04:07:52 +03:00
static void vss_on_reset ( void )
{
if ( cancel_delayed_work_sync ( & vss_timeout_work ) )
vss_respond_to_host ( HV_E_FAIL ) ;
vss_transaction . state = HVUTIL_DEVICE_INIT ;
}
2013-03-15 23:30:06 +04:00
int
hv_vss_init ( struct hv_util_service * srv )
{
2015-12-15 03:01:42 +03:00
if ( vmbus_proto_version < VERSION_WIN8_1 ) {
pr_warn ( " Integration service 'Backup (volume snapshot)' "
" not supported on this host version. \n " ) ;
return - ENOTSUPP ;
}
2013-03-15 23:30:06 +04:00
recv_buffer = srv - > recv_buffer ;
2016-02-27 02:13:19 +03:00
vss_transaction . recv_channel = srv - > channel ;
2013-03-15 23:30:06 +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:48 +03:00
vss_transaction . state = HVUTIL_DEVICE_INIT ;
2015-04-12 04:07:52 +03:00
hvt = hvutil_transport_init ( vss_devname , CN_VSS_IDX , CN_VSS_VAL ,
vss_on_msg , vss_on_reset ) ;
2016-11-07 00:14:10 +03:00
if ( ! hvt ) {
pr_warn ( " VSS: Failed to initialize transport \n " ) ;
2015-04-12 04:07:52 +03:00
return - EFAULT ;
2016-11-07 00:14:10 +03:00
}
2015-04-12 04:07:52 +03:00
2013-03-15 23:30:06 +04:00
return 0 ;
}
void hv_vss_deinit ( void )
{
2015-04-12 04:07:48 +03:00
vss_transaction . state = HVUTIL_DEVICE_DYING ;
2014-11-06 20:21:24 +03:00
cancel_delayed_work_sync ( & vss_timeout_work ) ;
2016-09-02 15:58:24 +03:00
cancel_work_sync ( & vss_handle_request_work ) ;
2015-04-12 04:07:52 +03:00
hvutil_transport_destroy ( hvt ) ;
2013-03-15 23:30:06 +04:00
}