2013-03-15 12:30:06 -07: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-11 18:07:39 -07:00
# include "hyperv_vmbus.h"
2013-07-02 10:31:30 -07:00
# define VSS_MAJOR 5
# define VSS_MINOR 0
2013-09-06 11:49:56 -07:00
# define VSS_VERSION (VSS_MAJOR << 16 | VSS_MINOR)
2013-07-02 10:31:30 -07:00
2014-11-06 18:21:24 +01:00
# define VSS_USERSPACE_TIMEOUT (msecs_to_jiffies(10 * 1000))
2013-03-15 12:30:06 -07:00
/*
2015-04-11 18:07:48 -07: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 12:30:06 -07:00
*
2015-04-11 18:07:48 -07: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 12:30:06 -07:00
*/
static struct {
2015-04-11 18:07:48 -07:00
int state ; /* hvutil_device_state */
2013-03-15 12:30:06 -07: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 */
2015-04-11 18:07:43 -07:00
void * vss_context ; /* for the channel callback */
2013-03-15 12:30:06 -07:00
} vss_transaction ;
static void vss_respond_to_host ( int error ) ;
static struct cb_id vss_id = { CN_VSS_IDX , CN_VSS_VAL } ;
static const char vss_name [ ] = " vss_kernel_module " ;
static __u8 * recv_buffer ;
static void vss_send_op ( struct work_struct * dummy ) ;
2014-11-06 18:21:24 +01:00
static void vss_timeout_func ( struct work_struct * dummy ) ;
static DECLARE_DELAYED_WORK ( vss_timeout_work , vss_timeout_func ) ;
2013-03-15 12:30:06 -07:00
static DECLARE_WORK ( vss_send_op_work , vss_send_op ) ;
/*
* Callback when data is received from user mode .
*/
2014-11-06 18:21:24 +01: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-11 18:07:43 -07:00
2015-04-11 18:07:48 -07:00
/* Transaction is finished, reset the state. */
if ( vss_transaction . state > HVUTIL_READY )
vss_transaction . state = HVUTIL_READY ;
2015-04-11 18:07:43 -07:00
hv_poll_channel ( vss_transaction . vss_context ,
hv_vss_onchannelcallback ) ;
2014-11-06 18:21:24 +01:00
}
2013-03-15 12:30:06 -07:00
static void
vss_cn_callback ( struct cn_msg * msg , struct netlink_skb_parms * nsp )
{
struct hv_vss_msg * vss_msg ;
vss_msg = ( struct hv_vss_msg * ) msg - > data ;
2015-04-11 18:07:48 -07:00
/*
* Don ' t process registration messages if we ' re in the middle of
* a transaction processing .
*/
if ( vss_transaction . state > HVUTIL_READY & &
vss_msg - > vss_hdr . operation = = VSS_OP_REGISTER )
return ;
if ( vss_transaction . state = = HVUTIL_DEVICE_INIT & &
vss_msg - > vss_hdr . operation = = VSS_OP_REGISTER ) {
2013-03-15 12:30:06 -07:00
pr_info ( " VSS daemon registered \n " ) ;
2015-04-11 18:07:48 -07:00
vss_transaction . state = HVUTIL_READY ;
} else if ( vss_transaction . state = = HVUTIL_USERSPACE_REQ ) {
vss_transaction . state = HVUTIL_USERSPACE_RECV ;
if ( cancel_delayed_work_sync ( & vss_timeout_work ) ) {
vss_respond_to_host ( vss_msg - > error ) ;
/* Transaction is finished, reset the state. */
vss_transaction . state = HVUTIL_READY ;
hv_poll_channel ( vss_transaction . vss_context ,
hv_vss_onchannelcallback ) ;
}
} else {
/* This is a spurious call! */
pr_warn ( " VSS: Transaction not active \n " ) ;
return ;
2013-03-15 12:30:06 -07:00
}
}
static void vss_send_op ( struct work_struct * dummy )
{
int op = vss_transaction . msg - > vss_hdr . operation ;
2014-11-06 18:21:25 +01:00
int rc ;
2013-03-15 12:30:06 -07:00
struct cn_msg * msg ;
struct hv_vss_msg * vss_msg ;
2015-04-11 18:07:48 -07:00
/* The transaction state is wrong. */
if ( vss_transaction . state ! = HVUTIL_HOSTMSG_RECEIVED )
return ;
2013-03-15 12:30:06 -07:00
msg = kzalloc ( sizeof ( * msg ) + sizeof ( * vss_msg ) , GFP_ATOMIC ) ;
if ( ! msg )
return ;
vss_msg = ( struct hv_vss_msg * ) msg - > data ;
msg - > id . idx = CN_VSS_IDX ;
msg - > id . val = CN_VSS_VAL ;
vss_msg - > vss_hdr . operation = op ;
msg - > len = sizeof ( struct hv_vss_msg ) ;
2015-04-11 18:07:48 -07:00
vss_transaction . state = HVUTIL_USERSPACE_REQ ;
2014-11-06 18:21:25 +01:00
rc = cn_netlink_send ( msg , 0 , 0 , GFP_ATOMIC ) ;
if ( rc ) {
pr_warn ( " VSS: failed to communicate to the daemon: %d \n " , rc ) ;
2015-04-11 18:07:48 -07:00
if ( cancel_delayed_work_sync ( & vss_timeout_work ) ) {
2014-11-06 18:21:25 +01:00
vss_respond_to_host ( HV_E_FAIL ) ;
2015-04-11 18:07:48 -07:00
vss_transaction . state = HVUTIL_READY ;
}
2014-11-06 18:21:25 +01:00
}
2015-04-11 18:07:48 -07:00
2013-03-15 12:30:06 -07:00
kfree ( msg ) ;
return ;
}
/*
* 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 ;
struct icmsg_hdr * icmsghdrp ;
struct icmsg_negotiate * negop = NULL ;
2015-04-11 18:07:48 -07:00
if ( vss_transaction . state > HVUTIL_READY ) {
2013-03-15 12:30:06 -07:00
/*
* We will defer processing this callback once
* the current transaction is complete .
*/
2015-04-11 18:07:43 -07:00
vss_transaction . vss_context = context ;
2013-03-15 12:30:06 -07:00
return ;
}
2015-04-11 18:07:43 -07:00
vss_transaction . vss_context = NULL ;
2013-03-15 12:30:06 -07:00
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 ) {
vmbus_prep_negotiate_resp ( icmsghdrp , negop ,
2013-09-06 11:49:56 -07:00
recv_buffer , UTIL_FW_VERSION ,
VSS_VERSION ) ;
2013-03-15 12:30:06 -07: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_channel = channel ;
vss_transaction . recv_req_id = requestid ;
vss_transaction . msg = ( struct hv_vss_msg * ) vss_msg ;
switch ( vss_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_FREEZE :
case VSS_OP_THAW :
2015-04-11 18:07:48 -07:00
if ( vss_transaction . state < HVUTIL_READY ) {
/* Userspace is not registered yet */
vss_respond_to_host ( HV_E_FAIL ) ;
return ;
}
vss_transaction . state = HVUTIL_HOSTMSG_RECEIVED ;
2013-03-15 12:30:06 -07:00
schedule_work ( & vss_send_op_work ) ;
2014-11-06 18:21:24 +01:00
schedule_delayed_work ( & vss_timeout_work ,
VSS_USERSPACE_TIMEOUT ) ;
2013-03-15 12:30:06 -07:00
return ;
case VSS_OP_HOT_BACKUP :
vss_msg - > vss_cf . flags =
VSS_HBU_NO_AUTO_RECOVERY ;
vss_respond_to_host ( 0 ) ;
return ;
case VSS_OP_GET_DM_INFO :
vss_msg - > dm_info . flags = 0 ;
vss_respond_to_host ( 0 ) ;
return ;
default :
vss_respond_to_host ( 0 ) ;
return ;
}
}
icmsghdrp - > icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE ;
vmbus_sendpacket ( channel , recv_buffer ,
recvlen , requestid ,
VM_PKT_DATA_INBAND , 0 ) ;
}
}
int
hv_vss_init ( struct hv_util_service * srv )
{
int err ;
err = cn_add_callback ( & vss_id , vss_name , vss_cn_callback ) ;
if ( err )
return err ;
recv_buffer = srv - > recv_buffer ;
/*
* 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-11 18:07:48 -07:00
vss_transaction . state = HVUTIL_DEVICE_INIT ;
2013-03-15 12:30:06 -07:00
return 0 ;
}
void hv_vss_deinit ( void )
{
2015-04-11 18:07:48 -07:00
vss_transaction . state = HVUTIL_DEVICE_DYING ;
2013-03-15 12:30:06 -07:00
cn_del_callback ( & vss_id ) ;
2014-11-06 18:21:24 +01:00
cancel_delayed_work_sync ( & vss_timeout_work ) ;
2013-03-15 12:30:06 -07:00
cancel_work_sync ( & vss_send_op_work ) ;
}