xref: /openbmc/linux/drivers/hv/hv_util.c (revision 96ec2939)
13b20eb23SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
246a97191SGreg Kroah-Hartman /*
346a97191SGreg Kroah-Hartman  * Copyright (c) 2010, Microsoft Corporation.
446a97191SGreg Kroah-Hartman  *
546a97191SGreg Kroah-Hartman  * Authors:
646a97191SGreg Kroah-Hartman  *   Haiyang Zhang <haiyangz@microsoft.com>
746a97191SGreg Kroah-Hartman  *   Hank Janssen  <hjanssen@microsoft.com>
846a97191SGreg Kroah-Hartman  */
946a97191SGreg Kroah-Hartman #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1046a97191SGreg Kroah-Hartman 
1146a97191SGreg Kroah-Hartman #include <linux/kernel.h>
1246a97191SGreg Kroah-Hartman #include <linux/init.h>
1346a97191SGreg Kroah-Hartman #include <linux/module.h>
1446a97191SGreg Kroah-Hartman #include <linux/slab.h>
1546a97191SGreg Kroah-Hartman #include <linux/sysctl.h>
1646a97191SGreg Kroah-Hartman #include <linux/reboot.h>
1746a97191SGreg Kroah-Hartman #include <linux/hyperv.h>
183716a49aSVitaly Kuznetsov #include <linux/clockchips.h>
193716a49aSVitaly Kuznetsov #include <linux/ptp_clock_kernel.h>
20305f7549SK. Y. Srinivasan #include <asm/mshyperv.h>
2146a97191SGreg Kroah-Hartman 
2201325476SK. Y. Srinivasan #include "hyperv_vmbus.h"
236741335bSK. Y. Srinivasan 
243a491605SK. Y. Srinivasan #define SD_MAJOR	3
253a491605SK. Y. Srinivasan #define SD_MINOR	0
263e9c7205SDexuan Cui #define SD_MINOR_1	1
27ffd1d4a4SDexuan Cui #define SD_MINOR_2	2
283e9c7205SDexuan Cui #define SD_VERSION_3_1	(SD_MAJOR << 16 | SD_MINOR_1)
29ffd1d4a4SDexuan Cui #define SD_VERSION_3_2	(SD_MAJOR << 16 | SD_MINOR_2)
303a491605SK. Y. Srinivasan #define SD_VERSION	(SD_MAJOR << 16 | SD_MINOR)
316741335bSK. Y. Srinivasan 
32abeda47eSAlex Ng #define SD_MAJOR_1	1
33abeda47eSAlex Ng #define SD_VERSION_1	(SD_MAJOR_1 << 16 | SD_MINOR)
343a491605SK. Y. Srinivasan 
358e1d2607SAlex Ng #define TS_MAJOR	4
363a491605SK. Y. Srinivasan #define TS_MINOR	0
373a491605SK. Y. Srinivasan #define TS_VERSION	(TS_MAJOR << 16 | TS_MINOR)
383a491605SK. Y. Srinivasan 
39abeda47eSAlex Ng #define TS_MAJOR_1	1
40abeda47eSAlex Ng #define TS_VERSION_1	(TS_MAJOR_1 << 16 | TS_MINOR)
413a491605SK. Y. Srinivasan 
428e1d2607SAlex Ng #define TS_MAJOR_3	3
438e1d2607SAlex Ng #define TS_VERSION_3	(TS_MAJOR_3 << 16 | TS_MINOR)
448e1d2607SAlex Ng 
453a491605SK. Y. Srinivasan #define HB_MAJOR	3
463a491605SK. Y. Srinivasan #define HB_MINOR	0
473a491605SK. Y. Srinivasan #define HB_VERSION	(HB_MAJOR << 16 | HB_MINOR)
483a491605SK. Y. Srinivasan 
49abeda47eSAlex Ng #define HB_MAJOR_1	1
50abeda47eSAlex Ng #define HB_VERSION_1	(HB_MAJOR_1 << 16 | HB_MINOR)
513a491605SK. Y. Srinivasan 
523a491605SK. Y. Srinivasan static int sd_srv_version;
533a491605SK. Y. Srinivasan static int ts_srv_version;
543a491605SK. Y. Srinivasan static int hb_srv_version;
55a1656454SAlex Ng 
56ffd1d4a4SDexuan Cui #define SD_VER_COUNT 4
57a1656454SAlex Ng static const int sd_versions[] = {
58ffd1d4a4SDexuan Cui 	SD_VERSION_3_2,
593e9c7205SDexuan Cui 	SD_VERSION_3_1,
60a1656454SAlex Ng 	SD_VERSION,
61a1656454SAlex Ng 	SD_VERSION_1
62a1656454SAlex Ng };
63a1656454SAlex Ng 
64a1656454SAlex Ng #define TS_VER_COUNT 3
65a1656454SAlex Ng static const int ts_versions[] = {
66a1656454SAlex Ng 	TS_VERSION,
67a1656454SAlex Ng 	TS_VERSION_3,
68a1656454SAlex Ng 	TS_VERSION_1
69a1656454SAlex Ng };
70a1656454SAlex Ng 
71a1656454SAlex Ng #define HB_VER_COUNT 2
72a1656454SAlex Ng static const int hb_versions[] = {
73a1656454SAlex Ng 	HB_VERSION,
74a1656454SAlex Ng 	HB_VERSION_1
75a1656454SAlex Ng };
76a1656454SAlex Ng 
77a1656454SAlex Ng #define FW_VER_COUNT 2
78a1656454SAlex Ng static const int fw_versions[] = {
79a1656454SAlex Ng 	UTIL_FW_VERSION,
80a1656454SAlex Ng 	UTIL_WS2K8_FW_VERSION
81a1656454SAlex Ng };
826741335bSK. Y. Srinivasan 
83ffd1d4a4SDexuan Cui /*
84ffd1d4a4SDexuan Cui  * Send the "hibernate" udev event in a thread context.
85ffd1d4a4SDexuan Cui  */
86ffd1d4a4SDexuan Cui struct hibernate_work_context {
87ffd1d4a4SDexuan Cui 	struct work_struct work;
88ffd1d4a4SDexuan Cui 	struct hv_device *dev;
89ffd1d4a4SDexuan Cui };
90ffd1d4a4SDexuan Cui 
91ffd1d4a4SDexuan Cui static struct hibernate_work_context hibernate_context;
92ffd1d4a4SDexuan Cui static bool hibernation_supported;
93ffd1d4a4SDexuan Cui 
send_hibernate_uevent(struct work_struct * work)94ffd1d4a4SDexuan Cui static void send_hibernate_uevent(struct work_struct *work)
95ffd1d4a4SDexuan Cui {
96ffd1d4a4SDexuan Cui 	char *uevent_env[2] = { "EVENT=hibernate", NULL };
97ffd1d4a4SDexuan Cui 	struct hibernate_work_context *ctx;
98ffd1d4a4SDexuan Cui 
99ffd1d4a4SDexuan Cui 	ctx = container_of(work, struct hibernate_work_context, work);
100ffd1d4a4SDexuan Cui 
101ffd1d4a4SDexuan Cui 	kobject_uevent_env(&ctx->dev->device.kobj, KOBJ_CHANGE, uevent_env);
102ffd1d4a4SDexuan Cui 
103ffd1d4a4SDexuan Cui 	pr_info("Sent hibernation uevent\n");
104ffd1d4a4SDexuan Cui }
105ffd1d4a4SDexuan Cui 
hv_shutdown_init(struct hv_util_service * srv)106ffd1d4a4SDexuan Cui static int hv_shutdown_init(struct hv_util_service *srv)
107ffd1d4a4SDexuan Cui {
108ffd1d4a4SDexuan Cui 	struct vmbus_channel *channel = srv->channel;
109ffd1d4a4SDexuan Cui 
110ffd1d4a4SDexuan Cui 	INIT_WORK(&hibernate_context.work, send_hibernate_uevent);
111ffd1d4a4SDexuan Cui 	hibernate_context.dev = channel->device_obj;
112ffd1d4a4SDexuan Cui 
113ffd1d4a4SDexuan Cui 	hibernation_supported = hv_is_hibernation_supported();
114ffd1d4a4SDexuan Cui 
115ffd1d4a4SDexuan Cui 	return 0;
116ffd1d4a4SDexuan Cui }
117ffd1d4a4SDexuan Cui 
11846a97191SGreg Kroah-Hartman static void shutdown_onchannelcallback(void *context);
11946a97191SGreg Kroah-Hartman static struct hv_util_service util_shutdown = {
12046a97191SGreg Kroah-Hartman 	.util_cb = shutdown_onchannelcallback,
121ffd1d4a4SDexuan Cui 	.util_init = hv_shutdown_init,
12246a97191SGreg Kroah-Hartman };
12346a97191SGreg Kroah-Hartman 
1243ba1eb17SVivek yadav static int hv_timesync_init(struct hv_util_service *srv);
12554e19d34SDexuan Cui static int hv_timesync_pre_suspend(void);
1263ba1eb17SVivek yadav static void hv_timesync_deinit(void);
1273ba1eb17SVivek yadav 
12846a97191SGreg Kroah-Hartman static void timesync_onchannelcallback(void *context);
12946a97191SGreg Kroah-Hartman static struct hv_util_service util_timesynch = {
13046a97191SGreg Kroah-Hartman 	.util_cb = timesync_onchannelcallback,
1313ba1eb17SVivek yadav 	.util_init = hv_timesync_init,
13254e19d34SDexuan Cui 	.util_pre_suspend = hv_timesync_pre_suspend,
1333ba1eb17SVivek yadav 	.util_deinit = hv_timesync_deinit,
13446a97191SGreg Kroah-Hartman };
13546a97191SGreg Kroah-Hartman 
13646a97191SGreg Kroah-Hartman static void heartbeat_onchannelcallback(void *context);
13746a97191SGreg Kroah-Hartman static struct hv_util_service util_heartbeat = {
13846a97191SGreg Kroah-Hartman 	.util_cb = heartbeat_onchannelcallback,
13946a97191SGreg Kroah-Hartman };
14046a97191SGreg Kroah-Hartman 
14146a97191SGreg Kroah-Hartman static struct hv_util_service util_kvp = {
14246a97191SGreg Kroah-Hartman 	.util_cb = hv_kvp_onchannelcallback,
14346a97191SGreg Kroah-Hartman 	.util_init = hv_kvp_init,
14454e19d34SDexuan Cui 	.util_pre_suspend = hv_kvp_pre_suspend,
14554e19d34SDexuan Cui 	.util_pre_resume = hv_kvp_pre_resume,
14646a97191SGreg Kroah-Hartman 	.util_deinit = hv_kvp_deinit,
14746a97191SGreg Kroah-Hartman };
14846a97191SGreg Kroah-Hartman 
14996dd86faSK. Y. Srinivasan static struct hv_util_service util_vss = {
15096dd86faSK. Y. Srinivasan 	.util_cb = hv_vss_onchannelcallback,
15196dd86faSK. Y. Srinivasan 	.util_init = hv_vss_init,
15254e19d34SDexuan Cui 	.util_pre_suspend = hv_vss_pre_suspend,
15354e19d34SDexuan Cui 	.util_pre_resume = hv_vss_pre_resume,
15496dd86faSK. Y. Srinivasan 	.util_deinit = hv_vss_deinit,
15596dd86faSK. Y. Srinivasan };
15696dd86faSK. Y. Srinivasan 
15701325476SK. Y. Srinivasan static struct hv_util_service util_fcopy = {
15801325476SK. Y. Srinivasan 	.util_cb = hv_fcopy_onchannelcallback,
15901325476SK. Y. Srinivasan 	.util_init = hv_fcopy_init,
16054e19d34SDexuan Cui 	.util_pre_suspend = hv_fcopy_pre_suspend,
16154e19d34SDexuan Cui 	.util_pre_resume = hv_fcopy_pre_resume,
16201325476SK. Y. Srinivasan 	.util_deinit = hv_fcopy_deinit,
16301325476SK. Y. Srinivasan };
16401325476SK. Y. Srinivasan 
perform_shutdown(struct work_struct * dummy)1653dd6cb49SK. Y. Srinivasan static void perform_shutdown(struct work_struct *dummy)
1663dd6cb49SK. Y. Srinivasan {
1673dd6cb49SK. Y. Srinivasan 	orderly_poweroff(true);
1683dd6cb49SK. Y. Srinivasan }
1693dd6cb49SK. Y. Srinivasan 
perform_restart(struct work_struct * dummy)1703e9c7205SDexuan Cui static void perform_restart(struct work_struct *dummy)
1713e9c7205SDexuan Cui {
1723e9c7205SDexuan Cui 	orderly_reboot();
1733e9c7205SDexuan Cui }
1743e9c7205SDexuan Cui 
1753dd6cb49SK. Y. Srinivasan /*
1763dd6cb49SK. Y. Srinivasan  * Perform the shutdown operation in a thread context.
1773dd6cb49SK. Y. Srinivasan  */
1783dd6cb49SK. Y. Srinivasan static DECLARE_WORK(shutdown_work, perform_shutdown);
1793dd6cb49SK. Y. Srinivasan 
1803e9c7205SDexuan Cui /*
1813e9c7205SDexuan Cui  * Perform the restart operation in a thread context.
1823e9c7205SDexuan Cui  */
1833e9c7205SDexuan Cui static DECLARE_WORK(restart_work, perform_restart);
1843e9c7205SDexuan Cui 
shutdown_onchannelcallback(void * context)18546a97191SGreg Kroah-Hartman static void shutdown_onchannelcallback(void *context)
18646a97191SGreg Kroah-Hartman {
18746a97191SGreg Kroah-Hartman 	struct vmbus_channel *channel = context;
1883e9c7205SDexuan Cui 	struct work_struct *work = NULL;
18946a97191SGreg Kroah-Hartman 	u32 recvlen;
19046a97191SGreg Kroah-Hartman 	u64 requestid;
19146a97191SGreg Kroah-Hartman 	u8  *shut_txf_buf = util_shutdown.recv_buffer;
19246a97191SGreg Kroah-Hartman 
19346a97191SGreg Kroah-Hartman 	struct shutdown_msg_data *shutdown_msg;
19446a97191SGreg Kroah-Hartman 
19546a97191SGreg Kroah-Hartman 	struct icmsg_hdr *icmsghdrp;
19646a97191SGreg Kroah-Hartman 
19706caa778SAndres Beltran 	if (vmbus_recvpacket(channel, shut_txf_buf, HV_HYP_PAGE_SIZE, &recvlen, &requestid)) {
19806caa778SAndres Beltran 		pr_err_ratelimited("Shutdown request received. Could not read into shut txf buf\n");
19906caa778SAndres Beltran 		return;
20006caa778SAndres Beltran 	}
20146a97191SGreg Kroah-Hartman 
20206caa778SAndres Beltran 	if (!recvlen)
20306caa778SAndres Beltran 		return;
20406caa778SAndres Beltran 
20506caa778SAndres Beltran 	/* Ensure recvlen is big enough to read header data */
20606caa778SAndres Beltran 	if (recvlen < ICMSG_HDR) {
20706caa778SAndres Beltran 		pr_err_ratelimited("Shutdown request received. Packet length too small: %d\n",
20806caa778SAndres Beltran 				   recvlen);
20906caa778SAndres Beltran 		return;
21006caa778SAndres Beltran 	}
21106caa778SAndres Beltran 
21206caa778SAndres Beltran 	icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[sizeof(struct vmbuspipe_hdr)];
21346a97191SGreg Kroah-Hartman 
21446a97191SGreg Kroah-Hartman 	if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
21506caa778SAndres Beltran 		if (vmbus_prep_negotiate_resp(icmsghdrp,
21606caa778SAndres Beltran 				shut_txf_buf, recvlen,
217a1656454SAlex Ng 				fw_versions, FW_VER_COUNT,
218a1656454SAlex Ng 				sd_versions, SD_VER_COUNT,
219a1656454SAlex Ng 				NULL, &sd_srv_version)) {
220a1656454SAlex Ng 			pr_info("Shutdown IC version %d.%d\n",
221a1656454SAlex Ng 				sd_srv_version >> 16,
222a1656454SAlex Ng 				sd_srv_version & 0xFFFF);
223a1656454SAlex Ng 		}
22406caa778SAndres Beltran 	} else if (icmsghdrp->icmsgtype == ICMSGTYPE_SHUTDOWN) {
22506caa778SAndres Beltran 		/* Ensure recvlen is big enough to contain shutdown_msg_data struct */
22606caa778SAndres Beltran 		if (recvlen < ICMSG_HDR + sizeof(struct shutdown_msg_data)) {
22706caa778SAndres Beltran 			pr_err_ratelimited("Invalid shutdown msg data. Packet length too small: %u\n",
22806caa778SAndres Beltran 					   recvlen);
22906caa778SAndres Beltran 			return;
23006caa778SAndres Beltran 		}
23106caa778SAndres Beltran 
23206caa778SAndres Beltran 		shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ICMSG_HDR];
23346a97191SGreg Kroah-Hartman 
2343e9c7205SDexuan Cui 		/*
2353e9c7205SDexuan Cui 		 * shutdown_msg->flags can be 0(shut down), 2(reboot),
2363e9c7205SDexuan Cui 		 * or 4(hibernate). It may bitwise-OR 1, which means
2373e9c7205SDexuan Cui 		 * performing the request by force. Linux always tries
2383e9c7205SDexuan Cui 		 * to perform the request by force.
2393e9c7205SDexuan Cui 		 */
24046a97191SGreg Kroah-Hartman 		switch (shutdown_msg->flags) {
24146a97191SGreg Kroah-Hartman 		case 0:
24246a97191SGreg Kroah-Hartman 		case 1:
24346a97191SGreg Kroah-Hartman 			icmsghdrp->status = HV_S_OK;
2443e9c7205SDexuan Cui 			work = &shutdown_work;
24506caa778SAndres Beltran 			pr_info("Shutdown request received - graceful shutdown initiated\n");
24646a97191SGreg Kroah-Hartman 			break;
2473e9c7205SDexuan Cui 		case 2:
2483e9c7205SDexuan Cui 		case 3:
2493e9c7205SDexuan Cui 			icmsghdrp->status = HV_S_OK;
2503e9c7205SDexuan Cui 			work = &restart_work;
25106caa778SAndres Beltran 			pr_info("Restart request received - graceful restart initiated\n");
2523e9c7205SDexuan Cui 			break;
253ffd1d4a4SDexuan Cui 		case 4:
254ffd1d4a4SDexuan Cui 		case 5:
255ffd1d4a4SDexuan Cui 			pr_info("Hibernation request received\n");
256ffd1d4a4SDexuan Cui 			icmsghdrp->status = hibernation_supported ?
257ffd1d4a4SDexuan Cui 				HV_S_OK : HV_E_FAIL;
258ffd1d4a4SDexuan Cui 			if (hibernation_supported)
259ffd1d4a4SDexuan Cui 				work = &hibernate_context.work;
260ffd1d4a4SDexuan Cui 			break;
26146a97191SGreg Kroah-Hartman 		default:
26246a97191SGreg Kroah-Hartman 			icmsghdrp->status = HV_E_FAIL;
26306caa778SAndres Beltran 			pr_info("Shutdown request received - Invalid request\n");
26446a97191SGreg Kroah-Hartman 			break;
26546a97191SGreg Kroah-Hartman 		}
26606caa778SAndres Beltran 	} else {
26706caa778SAndres Beltran 		icmsghdrp->status = HV_E_FAIL;
26806caa778SAndres Beltran 		pr_err_ratelimited("Shutdown request received. Invalid msg type: %d\n",
26906caa778SAndres Beltran 				   icmsghdrp->icmsgtype);
27046a97191SGreg Kroah-Hartman 	}
27146a97191SGreg Kroah-Hartman 
27246a97191SGreg Kroah-Hartman 	icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
27346a97191SGreg Kroah-Hartman 		| ICMSGHDRFLAG_RESPONSE;
27446a97191SGreg Kroah-Hartman 
27546a97191SGreg Kroah-Hartman 	vmbus_sendpacket(channel, shut_txf_buf,
27646a97191SGreg Kroah-Hartman 			 recvlen, requestid,
27746a97191SGreg Kroah-Hartman 			 VM_PKT_DATA_INBAND, 0);
27846a97191SGreg Kroah-Hartman 
2793e9c7205SDexuan Cui 	if (work)
2803e9c7205SDexuan Cui 		schedule_work(work);
28146a97191SGreg Kroah-Hartman }
28246a97191SGreg Kroah-Hartman 
28346a97191SGreg Kroah-Hartman /*
28446a97191SGreg Kroah-Hartman  * Set the host time in a process context.
28546a97191SGreg Kroah-Hartman  */
2861d10602dSVitaly Kuznetsov static struct work_struct adj_time_work;
28746a97191SGreg Kroah-Hartman 
2881d10602dSVitaly Kuznetsov /*
2891d10602dSVitaly Kuznetsov  * The last time sample, received from the host. PTP device responds to
2901d10602dSVitaly Kuznetsov  * requests by using this data and the current partition-wide time reference
2911d10602dSVitaly Kuznetsov  * count.
2921d10602dSVitaly Kuznetsov  */
2931d10602dSVitaly Kuznetsov static struct {
29446a97191SGreg Kroah-Hartman 	u64				host_time;
2958e1d2607SAlex Ng 	u64				ref_time;
2961d10602dSVitaly Kuznetsov 	spinlock_t			lock;
2971d10602dSVitaly Kuznetsov } host_ts;
2981d10602dSVitaly Kuznetsov 
reftime_to_ns(u64 reftime)29990b125f4SVineeth Pillai static inline u64 reftime_to_ns(u64 reftime)
3001d10602dSVitaly Kuznetsov {
30190b125f4SVineeth Pillai 	return (reftime - WLTIMEDELTA) * 100;
30290b125f4SVineeth Pillai }
30390b125f4SVineeth Pillai 
30490b125f4SVineeth Pillai /*
30590b125f4SVineeth Pillai  * Hard coded threshold for host timesync delay: 600 seconds
30690b125f4SVineeth Pillai  */
30790b125f4SVineeth Pillai static const u64 HOST_TIMESYNC_DELAY_THRESH = 600 * (u64)NSEC_PER_SEC;
30890b125f4SVineeth Pillai 
hv_get_adj_host_time(struct timespec64 * ts)30990b125f4SVineeth Pillai static int hv_get_adj_host_time(struct timespec64 *ts)
31090b125f4SVineeth Pillai {
31190b125f4SVineeth Pillai 	u64 newtime, reftime, timediff_adj;
3121d10602dSVitaly Kuznetsov 	unsigned long flags;
31390b125f4SVineeth Pillai 	int ret = 0;
3141d10602dSVitaly Kuznetsov 
3151d10602dSVitaly Kuznetsov 	spin_lock_irqsave(&host_ts.lock, flags);
3160af3e137SAndrea Parri 	reftime = hv_read_reference_counter();
31790b125f4SVineeth Pillai 
31890b125f4SVineeth Pillai 	/*
31990b125f4SVineeth Pillai 	 * We need to let the caller know that last update from host
32090b125f4SVineeth Pillai 	 * is older than the max allowable threshold. clock_gettime()
32190b125f4SVineeth Pillai 	 * and PTP ioctl do not have a documented error that we could
32290b125f4SVineeth Pillai 	 * return for this specific case. Use ESTALE to report this.
32390b125f4SVineeth Pillai 	 */
32490b125f4SVineeth Pillai 	timediff_adj = reftime - host_ts.ref_time;
32590b125f4SVineeth Pillai 	if (timediff_adj * 100 > HOST_TIMESYNC_DELAY_THRESH) {
32690b125f4SVineeth Pillai 		pr_warn_once("TIMESYNC IC: Stale time stamp, %llu nsecs old\n",
32790b125f4SVineeth Pillai 			     (timediff_adj * 100));
32890b125f4SVineeth Pillai 		ret = -ESTALE;
32990b125f4SVineeth Pillai 	}
33090b125f4SVineeth Pillai 
33190b125f4SVineeth Pillai 	newtime = host_ts.host_time + timediff_adj;
33290b125f4SVineeth Pillai 	*ts = ns_to_timespec64(reftime_to_ns(newtime));
3331d10602dSVitaly Kuznetsov 	spin_unlock_irqrestore(&host_ts.lock, flags);
3341d10602dSVitaly Kuznetsov 
33590b125f4SVineeth Pillai 	return ret;
3361d10602dSVitaly Kuznetsov }
33746a97191SGreg Kroah-Hartman 
hv_set_host_time(struct work_struct * work)33846a97191SGreg Kroah-Hartman static void hv_set_host_time(struct work_struct *work)
33946a97191SGreg Kroah-Hartman {
34046a97191SGreg Kroah-Hartman 
34190b125f4SVineeth Pillai 	struct timespec64 ts;
34290b125f4SVineeth Pillai 
34390b125f4SVineeth Pillai 	if (!hv_get_adj_host_time(&ts))
3441d10602dSVitaly Kuznetsov 		do_settimeofday64(&ts);
34546a97191SGreg Kroah-Hartman }
34646a97191SGreg Kroah-Hartman 
34746a97191SGreg Kroah-Hartman /*
34846a97191SGreg Kroah-Hartman  * Synchronize time with host after reboot, restore, etc.
34946a97191SGreg Kroah-Hartman  *
35046a97191SGreg Kroah-Hartman  * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
35146a97191SGreg Kroah-Hartman  * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
35246a97191SGreg Kroah-Hartman  * message after the timesync channel is opened. Since the hv_utils module is
3532e338f7eSAlex Ng  * loaded after hv_vmbus, the first message is usually missed. This bit is
3542e338f7eSAlex Ng  * considered a hard request to discipline the clock.
3552e338f7eSAlex Ng  *
3562e338f7eSAlex Ng  * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is
3572e338f7eSAlex Ng  * typically used as a hint to the guest. The guest is under no obligation
3582e338f7eSAlex Ng  * to discipline the clock.
35946a97191SGreg Kroah-Hartman  */
adj_guesttime(u64 hosttime,u64 reftime,u8 adj_flags)3603716a49aSVitaly Kuznetsov static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags)
36146a97191SGreg Kroah-Hartman {
3623716a49aSVitaly Kuznetsov 	unsigned long flags;
3633716a49aSVitaly Kuznetsov 	u64 cur_reftime;
36446a97191SGreg Kroah-Hartman 
3653ba1eb17SVivek yadav 	/*
3663716a49aSVitaly Kuznetsov 	 * Save the adjusted time sample from the host and the snapshot
3671d10602dSVitaly Kuznetsov 	 * of the current system time.
3683716a49aSVitaly Kuznetsov 	 */
3693716a49aSVitaly Kuznetsov 	spin_lock_irqsave(&host_ts.lock, flags);
3703716a49aSVitaly Kuznetsov 
3710af3e137SAndrea Parri 	cur_reftime = hv_read_reference_counter();
3723716a49aSVitaly Kuznetsov 	host_ts.host_time = hosttime;
3733716a49aSVitaly Kuznetsov 	host_ts.ref_time = cur_reftime;
3743716a49aSVitaly Kuznetsov 
3753716a49aSVitaly Kuznetsov 	/*
3763716a49aSVitaly Kuznetsov 	 * TimeSync v4 messages contain reference time (guest's Hyper-V
3773716a49aSVitaly Kuznetsov 	 * clocksource read when the time sample was generated), we can
3783716a49aSVitaly Kuznetsov 	 * improve the precision by adding the delta between now and the
3791d10602dSVitaly Kuznetsov 	 * time of generation. For older protocols we set
3801d10602dSVitaly Kuznetsov 	 * reftime == cur_reftime on call.
3813716a49aSVitaly Kuznetsov 	 */
3823716a49aSVitaly Kuznetsov 	host_ts.host_time += (cur_reftime - reftime);
3833716a49aSVitaly Kuznetsov 
3843716a49aSVitaly Kuznetsov 	spin_unlock_irqrestore(&host_ts.lock, flags);
3851d10602dSVitaly Kuznetsov 
3861d10602dSVitaly Kuznetsov 	/* Schedule work to do do_settimeofday64() */
3871d10602dSVitaly Kuznetsov 	if (adj_flags & ICTIMESYNCFLAG_SYNC)
3881d10602dSVitaly Kuznetsov 		schedule_work(&adj_time_work);
38946a97191SGreg Kroah-Hartman }
39046a97191SGreg Kroah-Hartman 
39146a97191SGreg Kroah-Hartman /*
39246a97191SGreg Kroah-Hartman  * Time Sync Channel message handler.
39346a97191SGreg Kroah-Hartman  */
timesync_onchannelcallback(void * context)39446a97191SGreg Kroah-Hartman static void timesync_onchannelcallback(void *context)
39546a97191SGreg Kroah-Hartman {
39646a97191SGreg Kroah-Hartman 	struct vmbus_channel *channel = context;
39746a97191SGreg Kroah-Hartman 	u32 recvlen;
39846a97191SGreg Kroah-Hartman 	u64 requestid;
39946a97191SGreg Kroah-Hartman 	struct icmsg_hdr *icmsghdrp;
40046a97191SGreg Kroah-Hartman 	struct ictimesync_data *timedatap;
4018e1d2607SAlex Ng 	struct ictimesync_ref_data *refdata;
40246a97191SGreg Kroah-Hartman 	u8 *time_txf_buf = util_timesynch.recv_buffer;
40346a97191SGreg Kroah-Hartman 
404b46b4a8aSVineeth Pillai 	/*
405b46b4a8aSVineeth Pillai 	 * Drain the ring buffer and use the last packet to update
406b46b4a8aSVineeth Pillai 	 * host_ts
407b46b4a8aSVineeth Pillai 	 */
408b46b4a8aSVineeth Pillai 	while (1) {
409b46b4a8aSVineeth Pillai 		int ret = vmbus_recvpacket(channel, time_txf_buf,
410b46b4a8aSVineeth Pillai 					   HV_HYP_PAGE_SIZE, &recvlen,
411b46b4a8aSVineeth Pillai 					   &requestid);
412b46b4a8aSVineeth Pillai 		if (ret) {
41306caa778SAndres Beltran 			pr_err_ratelimited("TimeSync IC pkt recv failed (Err: %d)\n",
414b46b4a8aSVineeth Pillai 					   ret);
415b46b4a8aSVineeth Pillai 			break;
416b46b4a8aSVineeth Pillai 		}
41746a97191SGreg Kroah-Hartman 
418b46b4a8aSVineeth Pillai 		if (!recvlen)
419b46b4a8aSVineeth Pillai 			break;
420b46b4a8aSVineeth Pillai 
42106caa778SAndres Beltran 		/* Ensure recvlen is big enough to read header data */
42206caa778SAndres Beltran 		if (recvlen < ICMSG_HDR) {
42306caa778SAndres Beltran 			pr_err_ratelimited("Timesync request received. Packet length too small: %d\n",
42406caa778SAndres Beltran 					   recvlen);
42506caa778SAndres Beltran 			break;
42606caa778SAndres Beltran 		}
42706caa778SAndres Beltran 
42846a97191SGreg Kroah-Hartman 		icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
42946a97191SGreg Kroah-Hartman 				sizeof(struct vmbuspipe_hdr)];
43046a97191SGreg Kroah-Hartman 
43146a97191SGreg Kroah-Hartman 		if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
43206caa778SAndres Beltran 			if (vmbus_prep_negotiate_resp(icmsghdrp,
43306caa778SAndres Beltran 						time_txf_buf, recvlen,
434a1656454SAlex Ng 						fw_versions, FW_VER_COUNT,
435a1656454SAlex Ng 						ts_versions, TS_VER_COUNT,
436a1656454SAlex Ng 						NULL, &ts_srv_version)) {
4371274a690SAlex Ng 				pr_info("TimeSync IC version %d.%d\n",
438a1656454SAlex Ng 					ts_srv_version >> 16,
439a1656454SAlex Ng 					ts_srv_version & 0xFFFF);
440a1656454SAlex Ng 			}
44106caa778SAndres Beltran 		} else if (icmsghdrp->icmsgtype == ICMSGTYPE_TIMESYNC) {
4428e1d2607SAlex Ng 			if (ts_srv_version > TS_VERSION_3) {
44306caa778SAndres Beltran 				/* Ensure recvlen is big enough to read ictimesync_ref_data */
44406caa778SAndres Beltran 				if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_ref_data)) {
44506caa778SAndres Beltran 					pr_err_ratelimited("Invalid ictimesync ref data. Length too small: %u\n",
44606caa778SAndres Beltran 							   recvlen);
44706caa778SAndres Beltran 					break;
44806caa778SAndres Beltran 				}
44906caa778SAndres Beltran 				refdata = (struct ictimesync_ref_data *)&time_txf_buf[ICMSG_HDR];
4508e1d2607SAlex Ng 
4518e1d2607SAlex Ng 				adj_guesttime(refdata->parenttime,
4528e1d2607SAlex Ng 						refdata->vmreferencetime,
4538e1d2607SAlex Ng 						refdata->flags);
4548e1d2607SAlex Ng 			} else {
45506caa778SAndres Beltran 				/* Ensure recvlen is big enough to read ictimesync_data */
45606caa778SAndres Beltran 				if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_data)) {
45706caa778SAndres Beltran 					pr_err_ratelimited("Invalid ictimesync data. Length too small: %u\n",
45806caa778SAndres Beltran 							   recvlen);
45906caa778SAndres Beltran 					break;
46006caa778SAndres Beltran 				}
46106caa778SAndres Beltran 				timedatap = (struct ictimesync_data *)&time_txf_buf[ICMSG_HDR];
46206caa778SAndres Beltran 
4638e1d2607SAlex Ng 				adj_guesttime(timedatap->parenttime,
4640af3e137SAndrea Parri 					      hv_read_reference_counter(),
4658e1d2607SAlex Ng 					      timedatap->flags);
4668e1d2607SAlex Ng 			}
46706caa778SAndres Beltran 		} else {
46806caa778SAndres Beltran 			icmsghdrp->status = HV_E_FAIL;
46906caa778SAndres Beltran 			pr_err_ratelimited("Timesync request received. Invalid msg type: %d\n",
47006caa778SAndres Beltran 					   icmsghdrp->icmsgtype);
47146a97191SGreg Kroah-Hartman 		}
47246a97191SGreg Kroah-Hartman 
47346a97191SGreg Kroah-Hartman 		icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
47446a97191SGreg Kroah-Hartman 			| ICMSGHDRFLAG_RESPONSE;
47546a97191SGreg Kroah-Hartman 
47646a97191SGreg Kroah-Hartman 		vmbus_sendpacket(channel, time_txf_buf,
47746a97191SGreg Kroah-Hartman 				 recvlen, requestid,
47846a97191SGreg Kroah-Hartman 				 VM_PKT_DATA_INBAND, 0);
47946a97191SGreg Kroah-Hartman 	}
48046a97191SGreg Kroah-Hartman }
48146a97191SGreg Kroah-Hartman 
48246a97191SGreg Kroah-Hartman /*
48346a97191SGreg Kroah-Hartman  * Heartbeat functionality.
48446a97191SGreg Kroah-Hartman  * Every two seconds, Hyper-V send us a heartbeat request message.
48546a97191SGreg Kroah-Hartman  * we respond to this message, and Hyper-V knows we are alive.
48646a97191SGreg Kroah-Hartman  */
heartbeat_onchannelcallback(void * context)48746a97191SGreg Kroah-Hartman static void heartbeat_onchannelcallback(void *context)
48846a97191SGreg Kroah-Hartman {
48946a97191SGreg Kroah-Hartman 	struct vmbus_channel *channel = context;
49046a97191SGreg Kroah-Hartman 	u32 recvlen;
49146a97191SGreg Kroah-Hartman 	u64 requestid;
49246a97191SGreg Kroah-Hartman 	struct icmsg_hdr *icmsghdrp;
49346a97191SGreg Kroah-Hartman 	struct heartbeat_msg_data *heartbeat_msg;
49446a97191SGreg Kroah-Hartman 	u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
49546a97191SGreg Kroah-Hartman 
496407a3aeeSLong Li 	while (1) {
497407a3aeeSLong Li 
49806caa778SAndres Beltran 		if (vmbus_recvpacket(channel, hbeat_txf_buf, HV_HYP_PAGE_SIZE,
49906caa778SAndres Beltran 				     &recvlen, &requestid)) {
50006caa778SAndres Beltran 			pr_err_ratelimited("Heartbeat request received. Could not read into hbeat txf buf\n");
50106caa778SAndres Beltran 			return;
50206caa778SAndres Beltran 		}
50346a97191SGreg Kroah-Hartman 
504407a3aeeSLong Li 		if (!recvlen)
505407a3aeeSLong Li 			break;
506407a3aeeSLong Li 
50706caa778SAndres Beltran 		/* Ensure recvlen is big enough to read header data */
50806caa778SAndres Beltran 		if (recvlen < ICMSG_HDR) {
509bdb49526SColin Ian King 			pr_err_ratelimited("Heartbeat request received. Packet length too small: %d\n",
51006caa778SAndres Beltran 					   recvlen);
51106caa778SAndres Beltran 			break;
51206caa778SAndres Beltran 		}
51306caa778SAndres Beltran 
51446a97191SGreg Kroah-Hartman 		icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
51546a97191SGreg Kroah-Hartman 				sizeof(struct vmbuspipe_hdr)];
51646a97191SGreg Kroah-Hartman 
51746a97191SGreg Kroah-Hartman 		if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
518a1656454SAlex Ng 			if (vmbus_prep_negotiate_resp(icmsghdrp,
51906caa778SAndres Beltran 					hbeat_txf_buf, recvlen,
520a1656454SAlex Ng 					fw_versions, FW_VER_COUNT,
521a1656454SAlex Ng 					hb_versions, HB_VER_COUNT,
522a1656454SAlex Ng 					NULL, &hb_srv_version)) {
523a1656454SAlex Ng 
5241274a690SAlex Ng 				pr_info("Heartbeat IC version %d.%d\n",
525a1656454SAlex Ng 					hb_srv_version >> 16,
526a1656454SAlex Ng 					hb_srv_version & 0xFFFF);
527a1656454SAlex Ng 			}
52806caa778SAndres Beltran 		} else if (icmsghdrp->icmsgtype == ICMSGTYPE_HEARTBEAT) {
52906caa778SAndres Beltran 			/*
53006caa778SAndres Beltran 			 * Ensure recvlen is big enough to read seq_num. Reserved area is not
53106caa778SAndres Beltran 			 * included in the check as the host may not fill it up entirely
53206caa778SAndres Beltran 			 */
53306caa778SAndres Beltran 			if (recvlen < ICMSG_HDR + sizeof(u64)) {
53406caa778SAndres Beltran 				pr_err_ratelimited("Invalid heartbeat msg data. Length too small: %u\n",
53506caa778SAndres Beltran 						   recvlen);
53606caa778SAndres Beltran 				break;
53706caa778SAndres Beltran 			}
53806caa778SAndres Beltran 			heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ICMSG_HDR];
53946a97191SGreg Kroah-Hartman 
54046a97191SGreg Kroah-Hartman 			heartbeat_msg->seq_num += 1;
54106caa778SAndres Beltran 		} else {
54206caa778SAndres Beltran 			icmsghdrp->status = HV_E_FAIL;
54306caa778SAndres Beltran 			pr_err_ratelimited("Heartbeat request received. Invalid msg type: %d\n",
54406caa778SAndres Beltran 					   icmsghdrp->icmsgtype);
54546a97191SGreg Kroah-Hartman 		}
54646a97191SGreg Kroah-Hartman 
54746a97191SGreg Kroah-Hartman 		icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
54846a97191SGreg Kroah-Hartman 			| ICMSGHDRFLAG_RESPONSE;
54946a97191SGreg Kroah-Hartman 
55046a97191SGreg Kroah-Hartman 		vmbus_sendpacket(channel, hbeat_txf_buf,
55146a97191SGreg Kroah-Hartman 				 recvlen, requestid,
55246a97191SGreg Kroah-Hartman 				 VM_PKT_DATA_INBAND, 0);
55346a97191SGreg Kroah-Hartman 	}
55446a97191SGreg Kroah-Hartman }
55546a97191SGreg Kroah-Hartman 
556061dc93eSBoqun Feng #define HV_UTIL_RING_SEND_SIZE VMBUS_RING_SIZE(3 * HV_HYP_PAGE_SIZE)
557061dc93eSBoqun Feng #define HV_UTIL_RING_RECV_SIZE VMBUS_RING_SIZE(3 * HV_HYP_PAGE_SIZE)
558061dc93eSBoqun Feng 
util_probe(struct hv_device * dev,const struct hv_vmbus_device_id * dev_id)55946a97191SGreg Kroah-Hartman static int util_probe(struct hv_device *dev,
56046a97191SGreg Kroah-Hartman 			const struct hv_vmbus_device_id *dev_id)
56146a97191SGreg Kroah-Hartman {
56246a97191SGreg Kroah-Hartman 	struct hv_util_service *srv =
56346a97191SGreg Kroah-Hartman 		(struct hv_util_service *)dev_id->driver_data;
56446a97191SGreg Kroah-Hartman 	int ret;
56546a97191SGreg Kroah-Hartman 
566b14d749aSHimadri Pandya 	srv->recv_buffer = kmalloc(HV_HYP_PAGE_SIZE * 4, GFP_KERNEL);
56746a97191SGreg Kroah-Hartman 	if (!srv->recv_buffer)
56846a97191SGreg Kroah-Hartman 		return -ENOMEM;
569b9830d12SK. Y. Srinivasan 	srv->channel = dev->channel;
57046a97191SGreg Kroah-Hartman 	if (srv->util_init) {
57146a97191SGreg Kroah-Hartman 		ret = srv->util_init(srv);
57246a97191SGreg Kroah-Hartman 		if (ret) {
57346a97191SGreg Kroah-Hartman 			ret = -ENODEV;
57446a97191SGreg Kroah-Hartman 			goto error1;
57546a97191SGreg Kroah-Hartman 		}
57646a97191SGreg Kroah-Hartman 	}
57746a97191SGreg Kroah-Hartman 
5787ae3e035SK. Y. Srinivasan 	/*
5797ae3e035SK. Y. Srinivasan 	 * The set of services managed by the util driver are not performance
5807ae3e035SK. Y. Srinivasan 	 * critical and do not need batched reading. Furthermore, some services
5817ae3e035SK. Y. Srinivasan 	 * such as KVP can only handle one message from the host at a time.
5827ae3e035SK. Y. Srinivasan 	 * Turn off batched reading for all util drivers before we open the
5837ae3e035SK. Y. Srinivasan 	 * channel.
5847ae3e035SK. Y. Srinivasan 	 */
585b71e3282SStephen Hemminger 	set_channel_read_mode(dev->channel, HV_CALL_DIRECT);
5867ae3e035SK. Y. Srinivasan 
58746a97191SGreg Kroah-Hartman 	hv_set_drvdata(dev, srv);
58818965663SDexuan Cui 
589061dc93eSBoqun Feng 	ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE,
590061dc93eSBoqun Feng 			 HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb,
5910541a225SHimadri Pandya 			 dev->channel);
59218965663SDexuan Cui 	if (ret)
59318965663SDexuan Cui 		goto error;
59418965663SDexuan Cui 
59546a97191SGreg Kroah-Hartman 	return 0;
59646a97191SGreg Kroah-Hartman 
59746a97191SGreg Kroah-Hartman error:
59846a97191SGreg Kroah-Hartman 	if (srv->util_deinit)
59946a97191SGreg Kroah-Hartman 		srv->util_deinit();
60046a97191SGreg Kroah-Hartman error1:
60146a97191SGreg Kroah-Hartman 	kfree(srv->recv_buffer);
60246a97191SGreg Kroah-Hartman 	return ret;
60346a97191SGreg Kroah-Hartman }
60446a97191SGreg Kroah-Hartman 
util_remove(struct hv_device * dev)605*96ec2939SDawei Li static void util_remove(struct hv_device *dev)
60646a97191SGreg Kroah-Hartman {
60746a97191SGreg Kroah-Hartman 	struct hv_util_service *srv = hv_get_drvdata(dev);
60846a97191SGreg Kroah-Hartman 
60946a97191SGreg Kroah-Hartman 	if (srv->util_deinit)
61046a97191SGreg Kroah-Hartman 		srv->util_deinit();
6115380b383SK. Y. Srinivasan 	vmbus_close(dev->channel);
61246a97191SGreg Kroah-Hartman 	kfree(srv->recv_buffer);
61346a97191SGreg Kroah-Hartman }
61446a97191SGreg Kroah-Hartman 
61554e19d34SDexuan Cui /*
61654e19d34SDexuan Cui  * When we're in util_suspend(), all the userspace processes have been frozen
61754e19d34SDexuan Cui  * (refer to hibernate() -> freeze_processes()). The userspace is thawed only
61854e19d34SDexuan Cui  * after the whole resume procedure, including util_resume(), finishes.
61954e19d34SDexuan Cui  */
util_suspend(struct hv_device * dev)62054e19d34SDexuan Cui static int util_suspend(struct hv_device *dev)
62154e19d34SDexuan Cui {
62254e19d34SDexuan Cui 	struct hv_util_service *srv = hv_get_drvdata(dev);
62354e19d34SDexuan Cui 	int ret = 0;
62454e19d34SDexuan Cui 
62554e19d34SDexuan Cui 	if (srv->util_pre_suspend) {
62654e19d34SDexuan Cui 		ret = srv->util_pre_suspend();
62754e19d34SDexuan Cui 		if (ret)
62854e19d34SDexuan Cui 			return ret;
62954e19d34SDexuan Cui 	}
63054e19d34SDexuan Cui 
63154e19d34SDexuan Cui 	vmbus_close(dev->channel);
63254e19d34SDexuan Cui 
63354e19d34SDexuan Cui 	return 0;
63454e19d34SDexuan Cui }
63554e19d34SDexuan Cui 
util_resume(struct hv_device * dev)63654e19d34SDexuan Cui static int util_resume(struct hv_device *dev)
63754e19d34SDexuan Cui {
63854e19d34SDexuan Cui 	struct hv_util_service *srv = hv_get_drvdata(dev);
63954e19d34SDexuan Cui 	int ret = 0;
64054e19d34SDexuan Cui 
64154e19d34SDexuan Cui 	if (srv->util_pre_resume) {
64254e19d34SDexuan Cui 		ret = srv->util_pre_resume();
64354e19d34SDexuan Cui 		if (ret)
64454e19d34SDexuan Cui 			return ret;
64554e19d34SDexuan Cui 	}
64654e19d34SDexuan Cui 
647061dc93eSBoqun Feng 	ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE,
648061dc93eSBoqun Feng 			 HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb,
64954e19d34SDexuan Cui 			 dev->channel);
65054e19d34SDexuan Cui 	return ret;
65154e19d34SDexuan Cui }
65254e19d34SDexuan Cui 
65346a97191SGreg Kroah-Hartman static const struct hv_vmbus_device_id id_table[] = {
65446a97191SGreg Kroah-Hartman 	/* Shutdown guid */
655d13984e5SK. Y. Srinivasan 	{ HV_SHUTDOWN_GUID,
656d13984e5SK. Y. Srinivasan 	  .driver_data = (unsigned long)&util_shutdown
657d13984e5SK. Y. Srinivasan 	},
65846a97191SGreg Kroah-Hartman 	/* Time synch guid */
659d13984e5SK. Y. Srinivasan 	{ HV_TS_GUID,
660d13984e5SK. Y. Srinivasan 	  .driver_data = (unsigned long)&util_timesynch
661d13984e5SK. Y. Srinivasan 	},
66246a97191SGreg Kroah-Hartman 	/* Heartbeat guid */
663d13984e5SK. Y. Srinivasan 	{ HV_HEART_BEAT_GUID,
664d13984e5SK. Y. Srinivasan 	  .driver_data = (unsigned long)&util_heartbeat
665d13984e5SK. Y. Srinivasan 	},
66646a97191SGreg Kroah-Hartman 	/* KVP guid */
667d13984e5SK. Y. Srinivasan 	{ HV_KVP_GUID,
668d13984e5SK. Y. Srinivasan 	  .driver_data = (unsigned long)&util_kvp
669d13984e5SK. Y. Srinivasan 	},
67096dd86faSK. Y. Srinivasan 	/* VSS GUID */
67196dd86faSK. Y. Srinivasan 	{ HV_VSS_GUID,
67296dd86faSK. Y. Srinivasan 	  .driver_data = (unsigned long)&util_vss
67396dd86faSK. Y. Srinivasan 	},
67401325476SK. Y. Srinivasan 	/* File copy GUID */
67501325476SK. Y. Srinivasan 	{ HV_FCOPY_GUID,
67601325476SK. Y. Srinivasan 	  .driver_data = (unsigned long)&util_fcopy
67701325476SK. Y. Srinivasan 	},
67846a97191SGreg Kroah-Hartman 	{ },
67946a97191SGreg Kroah-Hartman };
68046a97191SGreg Kroah-Hartman 
68146a97191SGreg Kroah-Hartman MODULE_DEVICE_TABLE(vmbus, id_table);
68246a97191SGreg Kroah-Hartman 
68346a97191SGreg Kroah-Hartman /* The one and only one */
68446a97191SGreg Kroah-Hartman static  struct hv_driver util_drv = {
6855c24ee89SHaiyang Zhang 	.name = "hv_utils",
68646a97191SGreg Kroah-Hartman 	.id_table = id_table,
68746a97191SGreg Kroah-Hartman 	.probe =  util_probe,
68846a97191SGreg Kroah-Hartman 	.remove =  util_remove,
68954e19d34SDexuan Cui 	.suspend = util_suspend,
69054e19d34SDexuan Cui 	.resume =  util_resume,
691af0a5646SArjan van de Ven 	.driver = {
692af0a5646SArjan van de Ven 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
693af0a5646SArjan van de Ven 	},
69446a97191SGreg Kroah-Hartman };
69546a97191SGreg Kroah-Hartman 
hv_ptp_enable(struct ptp_clock_info * info,struct ptp_clock_request * request,int on)6963716a49aSVitaly Kuznetsov static int hv_ptp_enable(struct ptp_clock_info *info,
6973716a49aSVitaly Kuznetsov 			 struct ptp_clock_request *request, int on)
6983716a49aSVitaly Kuznetsov {
6993716a49aSVitaly Kuznetsov 	return -EOPNOTSUPP;
7003716a49aSVitaly Kuznetsov }
7013716a49aSVitaly Kuznetsov 
hv_ptp_settime(struct ptp_clock_info * p,const struct timespec64 * ts)7023716a49aSVitaly Kuznetsov static int hv_ptp_settime(struct ptp_clock_info *p, const struct timespec64 *ts)
7033716a49aSVitaly Kuznetsov {
7043716a49aSVitaly Kuznetsov 	return -EOPNOTSUPP;
7053716a49aSVitaly Kuznetsov }
7063716a49aSVitaly Kuznetsov 
hv_ptp_adjfine(struct ptp_clock_info * ptp,long delta)70773aa29a2SJacob Keller static int hv_ptp_adjfine(struct ptp_clock_info *ptp, long delta)
7083716a49aSVitaly Kuznetsov {
7093716a49aSVitaly Kuznetsov 	return -EOPNOTSUPP;
7103716a49aSVitaly Kuznetsov }
hv_ptp_adjtime(struct ptp_clock_info * ptp,s64 delta)7113716a49aSVitaly Kuznetsov static int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
7123716a49aSVitaly Kuznetsov {
7133716a49aSVitaly Kuznetsov 	return -EOPNOTSUPP;
7143716a49aSVitaly Kuznetsov }
7153716a49aSVitaly Kuznetsov 
hv_ptp_gettime(struct ptp_clock_info * info,struct timespec64 * ts)7163716a49aSVitaly Kuznetsov static int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts)
7173716a49aSVitaly Kuznetsov {
71890b125f4SVineeth Pillai 	return hv_get_adj_host_time(ts);
7193716a49aSVitaly Kuznetsov }
7203716a49aSVitaly Kuznetsov 
7213716a49aSVitaly Kuznetsov static struct ptp_clock_info ptp_hyperv_info = {
7223716a49aSVitaly Kuznetsov 	.name		= "hyperv",
7233716a49aSVitaly Kuznetsov 	.enable         = hv_ptp_enable,
7243716a49aSVitaly Kuznetsov 	.adjtime        = hv_ptp_adjtime,
72573aa29a2SJacob Keller 	.adjfine        = hv_ptp_adjfine,
7263716a49aSVitaly Kuznetsov 	.gettime64      = hv_ptp_gettime,
7273716a49aSVitaly Kuznetsov 	.settime64      = hv_ptp_settime,
7283716a49aSVitaly Kuznetsov 	.owner		= THIS_MODULE,
7293716a49aSVitaly Kuznetsov };
7303716a49aSVitaly Kuznetsov 
7313716a49aSVitaly Kuznetsov static struct ptp_clock *hv_ptp_clock;
7323716a49aSVitaly Kuznetsov 
hv_timesync_init(struct hv_util_service * srv)7333ba1eb17SVivek yadav static int hv_timesync_init(struct hv_util_service *srv)
7343ba1eb17SVivek yadav {
7355a16dfc8SDexuan Cui 	spin_lock_init(&host_ts.lock);
7365a16dfc8SDexuan Cui 
7371d10602dSVitaly Kuznetsov 	INIT_WORK(&adj_time_work, hv_set_host_time);
7383716a49aSVitaly Kuznetsov 
7393716a49aSVitaly Kuznetsov 	/*
7403716a49aSVitaly Kuznetsov 	 * ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is
7413716a49aSVitaly Kuznetsov 	 * disabled but the driver is still useful without the PTP device
7423716a49aSVitaly Kuznetsov 	 * as it still handles the ICTIMESYNCFLAG_SYNC case.
7433716a49aSVitaly Kuznetsov 	 */
7443716a49aSVitaly Kuznetsov 	hv_ptp_clock = ptp_clock_register(&ptp_hyperv_info, NULL);
7453716a49aSVitaly Kuznetsov 	if (IS_ERR_OR_NULL(hv_ptp_clock)) {
746c6a8625fSYueHaibing 		pr_err("cannot register PTP clock: %d\n",
747c6a8625fSYueHaibing 		       PTR_ERR_OR_ZERO(hv_ptp_clock));
7483716a49aSVitaly Kuznetsov 		hv_ptp_clock = NULL;
7493716a49aSVitaly Kuznetsov 	}
7503716a49aSVitaly Kuznetsov 
7513ba1eb17SVivek yadav 	return 0;
7523ba1eb17SVivek yadav }
7533ba1eb17SVivek yadav 
hv_timesync_cancel_work(void)75454e19d34SDexuan Cui static void hv_timesync_cancel_work(void)
75554e19d34SDexuan Cui {
75654e19d34SDexuan Cui 	cancel_work_sync(&adj_time_work);
75754e19d34SDexuan Cui }
75854e19d34SDexuan Cui 
hv_timesync_pre_suspend(void)75954e19d34SDexuan Cui static int hv_timesync_pre_suspend(void)
76054e19d34SDexuan Cui {
76154e19d34SDexuan Cui 	hv_timesync_cancel_work();
76254e19d34SDexuan Cui 	return 0;
76354e19d34SDexuan Cui }
76454e19d34SDexuan Cui 
hv_timesync_deinit(void)7653ba1eb17SVivek yadav static void hv_timesync_deinit(void)
7663ba1eb17SVivek yadav {
7673716a49aSVitaly Kuznetsov 	if (hv_ptp_clock)
7683716a49aSVitaly Kuznetsov 		ptp_clock_unregister(hv_ptp_clock);
76954e19d34SDexuan Cui 
77054e19d34SDexuan Cui 	hv_timesync_cancel_work();
7713ba1eb17SVivek yadav }
7723ba1eb17SVivek yadav 
init_hyperv_utils(void)77346a97191SGreg Kroah-Hartman static int __init init_hyperv_utils(void)
77446a97191SGreg Kroah-Hartman {
77546a97191SGreg Kroah-Hartman 	pr_info("Registering HyperV Utility Driver\n");
77646a97191SGreg Kroah-Hartman 
77746a97191SGreg Kroah-Hartman 	return vmbus_driver_register(&util_drv);
77846a97191SGreg Kroah-Hartman }
77946a97191SGreg Kroah-Hartman 
exit_hyperv_utils(void)78046a97191SGreg Kroah-Hartman static void exit_hyperv_utils(void)
78146a97191SGreg Kroah-Hartman {
78246a97191SGreg Kroah-Hartman 	pr_info("De-Registered HyperV Utility Driver\n");
78346a97191SGreg Kroah-Hartman 
78446a97191SGreg Kroah-Hartman 	vmbus_driver_unregister(&util_drv);
78546a97191SGreg Kroah-Hartman }
78646a97191SGreg Kroah-Hartman 
78746a97191SGreg Kroah-Hartman module_init(init_hyperv_utils);
78846a97191SGreg Kroah-Hartman module_exit(exit_hyperv_utils);
78946a97191SGreg Kroah-Hartman 
79046a97191SGreg Kroah-Hartman MODULE_DESCRIPTION("Hyper-V Utilities");
79146a97191SGreg Kroah-Hartman MODULE_LICENSE("GPL");
792