2010-05-05 02:55:05 +04:00
/*
* Copyright ( c ) 2010 , 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 .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . , 59 Temple
* Place - Suite 330 , Boston , MA 02111 - 1307 USA .
*
* Authors :
* Haiyang Zhang < haiyangz @ microsoft . com >
* Hank Janssen < hjanssen @ microsoft . com >
*/
2011-03-30 00:58:49 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2010-05-05 02:55:05 +04:00
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/sysctl.h>
2010-05-04 19:26:23 +04:00
# include <linux/reboot.h>
2011-10-04 23:29:52 +04:00
# include <linux/hyperv.h>
2010-05-05 02:55:05 +04:00
2014-02-16 23:34:30 +04:00
# include "hyperv_vmbus.h"
2013-07-02 21:31:30 +04:00
2013-09-06 22:49:56 +04:00
# define SD_MAJOR 3
# define SD_MINOR 0
# define SD_VERSION (SD_MAJOR << 16 | SD_MINOR)
2013-07-02 21:31:30 +04:00
2016-09-08 15:24:12 +03:00
# define SD_MAJOR_1 1
# define SD_VERSION_1 (SD_MAJOR_1 << 16 | SD_MINOR)
2013-09-06 22:49:56 +04:00
2016-09-08 15:24:14 +03:00
# define TS_MAJOR 4
2013-09-06 22:49:56 +04:00
# define TS_MINOR 0
# define TS_VERSION (TS_MAJOR << 16 | TS_MINOR)
2016-09-08 15:24:12 +03:00
# define TS_MAJOR_1 1
# define TS_VERSION_1 (TS_MAJOR_1 << 16 | TS_MINOR)
2013-09-06 22:49:56 +04:00
2016-09-08 15:24:14 +03:00
# define TS_MAJOR_3 3
# define TS_VERSION_3 (TS_MAJOR_3 << 16 | TS_MINOR)
2013-09-06 22:49:56 +04:00
# define HB_MAJOR 3
2016-09-08 15:24:12 +03:00
# define HB_MINOR 0
2013-09-06 22:49:56 +04:00
# define HB_VERSION (HB_MAJOR << 16 | HB_MINOR)
2016-09-08 15:24:12 +03:00
# define HB_MAJOR_1 1
# define HB_VERSION_1 (HB_MAJOR_1 << 16 | HB_MINOR)
2013-09-06 22:49:56 +04:00
static int sd_srv_version ;
static int ts_srv_version ;
static int hb_srv_version ;
static int util_fw_version ;
2013-07-02 21:31:30 +04:00
2011-09-18 21:31:33 +04:00
static void shutdown_onchannelcallback ( void * context ) ;
static struct hv_util_service util_shutdown = {
. util_cb = shutdown_onchannelcallback ,
} ;
2016-09-09 10:42:30 +03:00
static int hv_timesync_init ( struct hv_util_service * srv ) ;
static void hv_timesync_deinit ( void ) ;
2011-09-18 21:31:33 +04:00
static void timesync_onchannelcallback ( void * context ) ;
static struct hv_util_service util_timesynch = {
. util_cb = timesync_onchannelcallback ,
2016-09-09 10:42:30 +03:00
. util_init = hv_timesync_init ,
. util_deinit = hv_timesync_deinit ,
2011-09-18 21:31:33 +04:00
} ;
static void heartbeat_onchannelcallback ( void * context ) ;
static struct hv_util_service util_heartbeat = {
. util_cb = heartbeat_onchannelcallback ,
} ;
static struct hv_util_service util_kvp = {
. util_cb = hv_kvp_onchannelcallback ,
. util_init = hv_kvp_init ,
. util_deinit = hv_kvp_deinit ,
} ;
2010-05-05 02:55:05 +04:00
2013-03-15 23:30:06 +04:00
static struct hv_util_service util_vss = {
. util_cb = hv_vss_onchannelcallback ,
. util_init = hv_vss_init ,
. util_deinit = hv_vss_deinit ,
} ;
2014-02-16 23:34:30 +04:00
static struct hv_util_service util_fcopy = {
. util_cb = hv_fcopy_onchannelcallback ,
. util_init = hv_fcopy_init ,
. util_deinit = hv_fcopy_deinit ,
} ;
2013-01-24 05:42:45 +04:00
static void perform_shutdown ( struct work_struct * dummy )
{
orderly_poweroff ( true ) ;
}
/*
* Perform the shutdown operation in a thread context .
*/
static DECLARE_WORK ( shutdown_work , perform_shutdown ) ;
2010-05-05 01:31:18 +04:00
static void shutdown_onchannelcallback ( void * context )
2010-05-05 02:55:05 +04:00
{
struct vmbus_channel * channel = context ;
2010-12-14 03:23:36 +03:00
u32 recvlen ;
2010-05-05 02:55:05 +04:00
u64 requestid ;
2013-09-22 02:27:34 +04:00
bool execute_shutdown = false ;
2011-09-18 21:31:33 +04:00
u8 * shut_txf_buf = util_shutdown . recv_buffer ;
2010-05-05 02:55:05 +04:00
struct shutdown_msg_data * shutdown_msg ;
struct icmsg_hdr * icmsghdrp ;
struct icmsg_negotiate * negop = NULL ;
2010-12-14 03:23:36 +03:00
vmbus_recvpacket ( channel , shut_txf_buf ,
PAGE_SIZE , & recvlen , & requestid ) ;
2010-05-05 02:55:05 +04:00
if ( recvlen > 0 ) {
2010-12-14 03:23:36 +03:00
icmsghdrp = ( struct icmsg_hdr * ) & shut_txf_buf [
2010-05-05 02:55:05 +04:00
sizeof ( struct vmbuspipe_hdr ) ] ;
if ( icmsghdrp - > icmsgtype = = ICMSGTYPE_NEGOTIATE ) {
2012-05-13 00:44:58 +04:00
vmbus_prep_negotiate_resp ( icmsghdrp , negop ,
2013-09-06 22:49:56 +04:00
shut_txf_buf , util_fw_version ,
sd_srv_version ) ;
2010-05-05 02:55:05 +04:00
} else {
2010-12-14 03:23:36 +03:00
shutdown_msg =
( struct shutdown_msg_data * ) & shut_txf_buf [
sizeof ( struct vmbuspipe_hdr ) +
sizeof ( struct icmsg_hdr ) ] ;
2010-05-05 02:55:05 +04:00
switch ( shutdown_msg - > flags ) {
case 0 :
case 1 :
icmsghdrp - > status = HV_S_OK ;
execute_shutdown = true ;
2011-03-30 00:58:49 +04:00
pr_info ( " Shutdown request received - "
2011-04-13 23:14:05 +04:00
" graceful shutdown initiated \n " ) ;
2010-05-05 02:55:05 +04:00
break ;
default :
icmsghdrp - > status = HV_E_FAIL ;
execute_shutdown = false ;
2011-03-30 00:58:49 +04:00
pr_info ( " Shutdown request received - "
" Invalid request \n " ) ;
2010-05-05 02:55:05 +04:00
break ;
2011-04-11 01:31:35 +04:00
}
2010-05-05 02:55:05 +04:00
}
icmsghdrp - > icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE ;
2010-12-14 03:23:36 +03:00
vmbus_sendpacket ( channel , shut_txf_buf ,
2010-05-05 02:55:05 +04:00
recvlen , requestid ,
2011-01-26 23:12:13 +03:00
VM_PKT_DATA_INBAND , 0 ) ;
2010-05-05 02:55:05 +04:00
}
if ( execute_shutdown = = true )
2013-01-24 05:42:45 +04:00
schedule_work ( & shutdown_work ) ;
2010-05-05 02:55:05 +04:00
}
2011-08-27 22:31:31 +04:00
/*
* Set the host time in a process context .
*/
struct adj_time_work {
struct work_struct work ;
u64 host_time ;
2016-09-08 15:24:14 +03:00
u64 ref_time ;
u8 flags ;
2011-08-27 22:31:31 +04:00
} ;
static void hv_set_host_time ( struct work_struct * work )
{
struct adj_time_work * wrk ;
2016-09-08 15:24:14 +03:00
s64 host_tns ;
u64 newtime ;
struct timespec host_ts ;
2011-08-27 22:31:31 +04:00
wrk = container_of ( work , struct adj_time_work , work ) ;
2016-09-08 15:24:14 +03:00
newtime = wrk - > host_time ;
if ( ts_srv_version > TS_VERSION_3 ) {
/*
* Some latency has been introduced since Hyper - V generated
* its time sample . Take that latency into account before
* using TSC reference time sample from Hyper - V .
*
* This sample is given by TimeSync v4 and above hosts .
*/
u64 current_tick ;
rdmsrl ( HV_X64_MSR_TIME_REF_COUNT , current_tick ) ;
newtime + = ( current_tick - wrk - > ref_time ) ;
}
host_tns = ( newtime - WLTIMEDELTA ) * 100 ;
host_ts = ns_to_timespec ( host_tns ) ;
do_settimeofday ( & host_ts ) ;
2011-08-27 22:31:31 +04:00
}
2010-05-11 19:11:24 +04:00
/*
* Synchronize time with host after reboot , restore , etc .
*
* ICTIMESYNCFLAG_SYNC flag bit indicates reboot , restore events of the VM .
* After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
* message after the timesync channel is opened . Since the hv_utils module is
2016-09-08 15:24:13 +03:00
* loaded after hv_vmbus , the first message is usually missed . This bit is
* considered a hard request to discipline the clock .
*
* ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host . This is
* typically used as a hint to the guest . The guest is under no obligation
* to discipline the clock .
2010-05-11 19:11:24 +04:00
*/
2016-09-09 10:42:30 +03:00
static struct adj_time_work wrk ;
2016-09-08 15:24:14 +03:00
static inline void adj_guesttime ( u64 hosttime , u64 reftime , u8 flags )
2010-05-11 19:11:24 +04:00
{
2016-09-09 10:42:30 +03:00
/*
* This check is safe since we are executing in the
* interrupt context and time synch messages arre always
* delivered on the same CPU .
*/
if ( work_pending ( & wrk . work ) )
2011-08-27 22:31:31 +04:00
return ;
2016-09-09 10:42:30 +03:00
wrk . host_time = hosttime ;
wrk . ref_time = reftime ;
wrk . flags = flags ;
2016-09-08 15:24:13 +03:00
if ( ( flags & ( ICTIMESYNCFLAG_SYNC | ICTIMESYNCFLAG_SAMPLE ) ) ! = 0 ) {
2016-09-09 10:42:30 +03:00
schedule_work ( & wrk . work ) ;
}
2010-05-05 23:23:46 +04:00
}
/*
* Time Sync Channel message handler .
*/
static void timesync_onchannelcallback ( void * context )
{
struct vmbus_channel * channel = context ;
2010-12-14 03:23:36 +03:00
u32 recvlen ;
2010-05-05 23:23:46 +04:00
u64 requestid ;
struct icmsg_hdr * icmsghdrp ;
struct ictimesync_data * timedatap ;
2016-09-08 15:24:14 +03:00
struct ictimesync_ref_data * refdata ;
2011-09-18 21:31:33 +04:00
u8 * time_txf_buf = util_timesynch . recv_buffer ;
2013-09-06 22:49:56 +04:00
struct icmsg_negotiate * negop = NULL ;
2010-05-05 23:23:46 +04:00
2010-12-14 03:23:36 +03:00
vmbus_recvpacket ( channel , time_txf_buf ,
PAGE_SIZE , & recvlen , & requestid ) ;
2010-05-05 23:23:46 +04:00
if ( recvlen > 0 ) {
2010-12-14 03:23:36 +03:00
icmsghdrp = ( struct icmsg_hdr * ) & time_txf_buf [
2010-05-05 23:23:46 +04:00
sizeof ( struct vmbuspipe_hdr ) ] ;
if ( icmsghdrp - > icmsgtype = = ICMSGTYPE_NEGOTIATE ) {
2013-09-06 22:49:56 +04:00
vmbus_prep_negotiate_resp ( icmsghdrp , negop ,
time_txf_buf ,
util_fw_version ,
ts_srv_version ) ;
2016-09-08 15:24:14 +03:00
pr_info ( " Using TimeSync version %d.%d \n " ,
ts_srv_version > > 16 , ts_srv_version & 0xFFFF ) ;
2010-05-05 23:23:46 +04:00
} else {
2016-09-08 15:24:14 +03:00
if ( ts_srv_version > TS_VERSION_3 ) {
refdata = ( struct ictimesync_ref_data * )
& time_txf_buf [
sizeof ( struct vmbuspipe_hdr ) +
sizeof ( struct icmsg_hdr ) ] ;
adj_guesttime ( refdata - > parenttime ,
refdata - > vmreferencetime ,
refdata - > flags ) ;
} else {
timedatap = ( struct ictimesync_data * )
& time_txf_buf [
sizeof ( struct vmbuspipe_hdr ) +
sizeof ( struct icmsg_hdr ) ] ;
adj_guesttime ( timedatap - > parenttime ,
0 ,
timedatap - > flags ) ;
}
2010-05-05 23:23:46 +04:00
}
icmsghdrp - > icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE ;
2010-12-14 03:23:36 +03:00
vmbus_sendpacket ( channel , time_txf_buf ,
2010-05-05 23:23:46 +04:00
recvlen , requestid ,
2011-01-26 23:12:13 +03:00
VM_PKT_DATA_INBAND , 0 ) ;
2010-05-05 23:23:46 +04:00
}
}
2010-05-16 01:39:58 +04:00
/*
* Heartbeat functionality .
* Every two seconds , Hyper - V send us a heartbeat request message .
* we respond to this message , and Hyper - V knows we are alive .
*/
static void heartbeat_onchannelcallback ( void * context )
{
struct vmbus_channel * channel = context ;
2010-12-14 03:23:36 +03:00
u32 recvlen ;
2010-05-16 01:39:58 +04:00
u64 requestid ;
struct icmsg_hdr * icmsghdrp ;
struct heartbeat_msg_data * heartbeat_msg ;
2011-09-18 21:31:33 +04:00
u8 * hbeat_txf_buf = util_heartbeat . recv_buffer ;
2013-09-06 22:49:56 +04:00
struct icmsg_negotiate * negop = NULL ;
2010-05-16 01:39:58 +04:00
2010-12-14 03:23:36 +03:00
vmbus_recvpacket ( channel , hbeat_txf_buf ,
PAGE_SIZE , & recvlen , & requestid ) ;
2010-05-16 01:39:58 +04:00
if ( recvlen > 0 ) {
2010-12-14 03:23:36 +03:00
icmsghdrp = ( struct icmsg_hdr * ) & hbeat_txf_buf [
2010-05-16 01:39:58 +04:00
sizeof ( struct vmbuspipe_hdr ) ] ;
if ( icmsghdrp - > icmsgtype = = ICMSGTYPE_NEGOTIATE ) {
2013-09-06 22:49:56 +04:00
vmbus_prep_negotiate_resp ( icmsghdrp , negop ,
hbeat_txf_buf , util_fw_version ,
hb_srv_version ) ;
2010-05-16 01:39:58 +04:00
} else {
2010-12-14 03:23:36 +03:00
heartbeat_msg =
( struct heartbeat_msg_data * ) & hbeat_txf_buf [
sizeof ( struct vmbuspipe_hdr ) +
sizeof ( struct icmsg_hdr ) ] ;
2010-05-16 01:39:58 +04:00
heartbeat_msg - > seq_num + = 1 ;
}
icmsghdrp - > icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE ;
2010-12-14 03:23:36 +03:00
vmbus_sendpacket ( channel , hbeat_txf_buf ,
2010-05-16 01:39:58 +04:00
recvlen , requestid ,
2011-01-26 23:12:13 +03:00
VM_PKT_DATA_INBAND , 0 ) ;
2010-05-16 01:39:58 +04:00
}
}
2010-05-05 23:23:46 +04:00
2011-09-13 21:59:38 +04:00
static int util_probe ( struct hv_device * dev ,
const struct hv_vmbus_device_id * dev_id )
2011-08-25 20:48:36 +04:00
{
2011-09-18 21:31:33 +04:00
struct hv_util_service * srv =
( struct hv_util_service * ) dev_id - > driver_data ;
int ret ;
2014-07-08 03:34:25 +04:00
srv - > recv_buffer = kmalloc ( PAGE_SIZE * 4 , GFP_KERNEL ) ;
2011-09-18 21:31:33 +04:00
if ( ! srv - > recv_buffer )
return - ENOMEM ;
2016-02-27 02:13:19 +03:00
srv - > channel = dev - > channel ;
2011-09-18 21:31:33 +04:00
if ( srv - > util_init ) {
ret = srv - > util_init ( srv ) ;
if ( ret ) {
2011-09-18 21:31:34 +04:00
ret = - ENODEV ;
goto error1 ;
2011-09-18 21:31:33 +04:00
}
}
2012-12-01 18:46:34 +04:00
/*
* The set of services managed by the util driver are not performance
* critical and do not need batched reading . Furthermore , some services
* such as KVP can only handle one message from the host at a time .
* Turn off batched reading for all util drivers before we open the
* channel .
*/
set_channel_read_state ( dev - > channel , false ) ;
2011-09-18 21:31:33 +04:00
hv_set_drvdata ( dev , srv ) ;
2015-02-27 22:25:58 +03:00
2013-09-06 22:49:56 +04:00
/*
* Based on the host ; initialize the framework and
* service version numbers we will negotiate .
*/
switch ( vmbus_proto_version ) {
case ( VERSION_WS2008 ) :
util_fw_version = UTIL_WS2K8_FW_VERSION ;
2016-09-08 15:24:12 +03:00
sd_srv_version = SD_VERSION_1 ;
ts_srv_version = TS_VERSION_1 ;
hb_srv_version = HB_VERSION_1 ;
2013-09-06 22:49:56 +04:00
break ;
2016-09-08 15:24:14 +03:00
case ( VERSION_WIN10 ) :
2013-09-06 22:49:56 +04:00
util_fw_version = UTIL_FW_VERSION ;
sd_srv_version = SD_VERSION ;
ts_srv_version = TS_VERSION ;
hb_srv_version = HB_VERSION ;
2016-09-08 15:24:14 +03:00
break ;
default :
util_fw_version = UTIL_FW_VERSION ;
sd_srv_version = SD_VERSION ;
ts_srv_version = TS_VERSION_3 ;
hb_srv_version = HB_VERSION ;
2013-09-06 22:49:56 +04:00
}
2015-02-27 22:25:58 +03:00
ret = vmbus_open ( dev - > channel , 4 * PAGE_SIZE , 4 * PAGE_SIZE , NULL , 0 ,
srv - > util_cb , dev - > channel ) ;
if ( ret )
goto error ;
2011-08-25 20:48:36 +04:00
return 0 ;
2011-09-18 21:31:34 +04:00
error :
if ( srv - > util_deinit )
srv - > util_deinit ( ) ;
error1 :
kfree ( srv - > recv_buffer ) ;
return ret ;
2011-08-25 20:48:36 +04:00
}
static int util_remove ( struct hv_device * dev )
{
2011-09-18 21:31:33 +04:00
struct hv_util_service * srv = hv_get_drvdata ( dev ) ;
if ( srv - > util_deinit )
srv - > util_deinit ( ) ;
2015-02-28 22:18:20 +03:00
vmbus_close ( dev - > channel ) ;
2011-09-18 21:31:33 +04:00
kfree ( srv - > recv_buffer ) ;
2011-08-25 20:48:36 +04:00
return 0 ;
}
static const struct hv_vmbus_device_id id_table [ ] = {
2011-08-25 22:41:33 +04:00
/* Shutdown guid */
2013-01-24 05:42:41 +04:00
{ HV_SHUTDOWN_GUID ,
. driver_data = ( unsigned long ) & util_shutdown
} ,
2011-08-25 22:41:33 +04:00
/* Time synch guid */
2013-01-24 05:42:41 +04:00
{ HV_TS_GUID ,
. driver_data = ( unsigned long ) & util_timesynch
} ,
2011-08-25 22:41:33 +04:00
/* Heartbeat guid */
2013-01-24 05:42:41 +04:00
{ HV_HEART_BEAT_GUID ,
. driver_data = ( unsigned long ) & util_heartbeat
} ,
2011-08-25 22:41:33 +04:00
/* KVP guid */
2013-01-24 05:42:41 +04:00
{ HV_KVP_GUID ,
. driver_data = ( unsigned long ) & util_kvp
} ,
2013-03-15 23:30:06 +04:00
/* VSS GUID */
{ HV_VSS_GUID ,
. driver_data = ( unsigned long ) & util_vss
} ,
2014-02-16 23:34:30 +04:00
/* File copy GUID */
{ HV_FCOPY_GUID ,
. driver_data = ( unsigned long ) & util_fcopy
} ,
2011-08-25 22:41:33 +04:00
{ } ,
2011-08-25 20:48:36 +04:00
} ;
MODULE_DEVICE_TABLE ( vmbus , id_table ) ;
/* The one and only one */
static struct hv_driver util_drv = {
2011-08-26 02:07:32 +04:00
. name = " hv_util " ,
2011-08-25 20:48:36 +04:00
. id_table = id_table ,
. probe = util_probe ,
. remove = util_remove ,
} ;
2016-09-09 10:42:30 +03:00
static int hv_timesync_init ( struct hv_util_service * srv )
{
INIT_WORK ( & wrk . work , hv_set_host_time ) ;
return 0 ;
}
static void hv_timesync_deinit ( void )
{
cancel_work_sync ( & wrk . work ) ;
}
2010-05-05 02:55:05 +04:00
static int __init init_hyperv_utils ( void )
{
2011-03-30 00:58:49 +04:00
pr_info ( " Registering HyperV Utility Driver \n " ) ;
2010-05-05 02:55:05 +04:00
2011-09-18 21:31:34 +04:00
return vmbus_driver_register ( & util_drv ) ;
2010-05-05 02:55:05 +04:00
}
static void exit_hyperv_utils ( void )
{
2011-03-30 00:58:49 +04:00
pr_info ( " De-Registered HyperV Utility Driver \n " ) ;
2010-05-05 02:55:05 +04:00
2011-08-26 02:07:32 +04:00
vmbus_driver_unregister ( & util_drv ) ;
2010-05-05 02:55:05 +04:00
}
module_init ( init_hyperv_utils ) ;
module_exit ( exit_hyperv_utils ) ;
MODULE_DESCRIPTION ( " Hyper-V Utilities " ) ;
MODULE_LICENSE ( " GPL " ) ;