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,
144*89fcec5eSMichael Kelley .util_init_transport = hv_kvp_init_transport,
14554e19d34SDexuan Cui .util_pre_suspend = hv_kvp_pre_suspend,
14654e19d34SDexuan Cui .util_pre_resume = hv_kvp_pre_resume,
14746a97191SGreg Kroah-Hartman .util_deinit = hv_kvp_deinit,
14846a97191SGreg Kroah-Hartman };
14946a97191SGreg Kroah-Hartman
15096dd86faSK. Y. Srinivasan static struct hv_util_service util_vss = {
15196dd86faSK. Y. Srinivasan .util_cb = hv_vss_onchannelcallback,
15296dd86faSK. Y. Srinivasan .util_init = hv_vss_init,
153*89fcec5eSMichael Kelley .util_init_transport = hv_vss_init_transport,
15454e19d34SDexuan Cui .util_pre_suspend = hv_vss_pre_suspend,
15554e19d34SDexuan Cui .util_pre_resume = hv_vss_pre_resume,
15696dd86faSK. Y. Srinivasan .util_deinit = hv_vss_deinit,
15796dd86faSK. Y. Srinivasan };
15896dd86faSK. Y. Srinivasan
15901325476SK. Y. Srinivasan static struct hv_util_service util_fcopy = {
16001325476SK. Y. Srinivasan .util_cb = hv_fcopy_onchannelcallback,
16101325476SK. Y. Srinivasan .util_init = hv_fcopy_init,
16254e19d34SDexuan Cui .util_pre_suspend = hv_fcopy_pre_suspend,
16354e19d34SDexuan Cui .util_pre_resume = hv_fcopy_pre_resume,
16401325476SK. Y. Srinivasan .util_deinit = hv_fcopy_deinit,
16501325476SK. Y. Srinivasan };
16601325476SK. Y. Srinivasan
perform_shutdown(struct work_struct * dummy)1673dd6cb49SK. Y. Srinivasan static void perform_shutdown(struct work_struct *dummy)
1683dd6cb49SK. Y. Srinivasan {
1693dd6cb49SK. Y. Srinivasan orderly_poweroff(true);
1703dd6cb49SK. Y. Srinivasan }
1713dd6cb49SK. Y. Srinivasan
perform_restart(struct work_struct * dummy)1723e9c7205SDexuan Cui static void perform_restart(struct work_struct *dummy)
1733e9c7205SDexuan Cui {
1743e9c7205SDexuan Cui orderly_reboot();
1753e9c7205SDexuan Cui }
1763e9c7205SDexuan Cui
1773dd6cb49SK. Y. Srinivasan /*
1783dd6cb49SK. Y. Srinivasan * Perform the shutdown operation in a thread context.
1793dd6cb49SK. Y. Srinivasan */
1803dd6cb49SK. Y. Srinivasan static DECLARE_WORK(shutdown_work, perform_shutdown);
1813dd6cb49SK. Y. Srinivasan
1823e9c7205SDexuan Cui /*
1833e9c7205SDexuan Cui * Perform the restart operation in a thread context.
1843e9c7205SDexuan Cui */
1853e9c7205SDexuan Cui static DECLARE_WORK(restart_work, perform_restart);
1863e9c7205SDexuan Cui
shutdown_onchannelcallback(void * context)18746a97191SGreg Kroah-Hartman static void shutdown_onchannelcallback(void *context)
18846a97191SGreg Kroah-Hartman {
18946a97191SGreg Kroah-Hartman struct vmbus_channel *channel = context;
1903e9c7205SDexuan Cui struct work_struct *work = NULL;
19146a97191SGreg Kroah-Hartman u32 recvlen;
19246a97191SGreg Kroah-Hartman u64 requestid;
19346a97191SGreg Kroah-Hartman u8 *shut_txf_buf = util_shutdown.recv_buffer;
19446a97191SGreg Kroah-Hartman
19546a97191SGreg Kroah-Hartman struct shutdown_msg_data *shutdown_msg;
19646a97191SGreg Kroah-Hartman
19746a97191SGreg Kroah-Hartman struct icmsg_hdr *icmsghdrp;
19846a97191SGreg Kroah-Hartman
19906caa778SAndres Beltran if (vmbus_recvpacket(channel, shut_txf_buf, HV_HYP_PAGE_SIZE, &recvlen, &requestid)) {
20006caa778SAndres Beltran pr_err_ratelimited("Shutdown request received. Could not read into shut txf buf\n");
20106caa778SAndres Beltran return;
20206caa778SAndres Beltran }
20346a97191SGreg Kroah-Hartman
20406caa778SAndres Beltran if (!recvlen)
20506caa778SAndres Beltran return;
20606caa778SAndres Beltran
20706caa778SAndres Beltran /* Ensure recvlen is big enough to read header data */
20806caa778SAndres Beltran if (recvlen < ICMSG_HDR) {
20906caa778SAndres Beltran pr_err_ratelimited("Shutdown request received. Packet length too small: %d\n",
21006caa778SAndres Beltran recvlen);
21106caa778SAndres Beltran return;
21206caa778SAndres Beltran }
21306caa778SAndres Beltran
21406caa778SAndres Beltran icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[sizeof(struct vmbuspipe_hdr)];
21546a97191SGreg Kroah-Hartman
21646a97191SGreg Kroah-Hartman if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
21706caa778SAndres Beltran if (vmbus_prep_negotiate_resp(icmsghdrp,
21806caa778SAndres Beltran shut_txf_buf, recvlen,
219a1656454SAlex Ng fw_versions, FW_VER_COUNT,
220a1656454SAlex Ng sd_versions, SD_VER_COUNT,
221a1656454SAlex Ng NULL, &sd_srv_version)) {
222a1656454SAlex Ng pr_info("Shutdown IC version %d.%d\n",
223a1656454SAlex Ng sd_srv_version >> 16,
224a1656454SAlex Ng sd_srv_version & 0xFFFF);
225a1656454SAlex Ng }
22606caa778SAndres Beltran } else if (icmsghdrp->icmsgtype == ICMSGTYPE_SHUTDOWN) {
22706caa778SAndres Beltran /* Ensure recvlen is big enough to contain shutdown_msg_data struct */
22806caa778SAndres Beltran if (recvlen < ICMSG_HDR + sizeof(struct shutdown_msg_data)) {
22906caa778SAndres Beltran pr_err_ratelimited("Invalid shutdown msg data. Packet length too small: %u\n",
23006caa778SAndres Beltran recvlen);
23106caa778SAndres Beltran return;
23206caa778SAndres Beltran }
23306caa778SAndres Beltran
23406caa778SAndres Beltran shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ICMSG_HDR];
23546a97191SGreg Kroah-Hartman
2363e9c7205SDexuan Cui /*
2373e9c7205SDexuan Cui * shutdown_msg->flags can be 0(shut down), 2(reboot),
2383e9c7205SDexuan Cui * or 4(hibernate). It may bitwise-OR 1, which means
2393e9c7205SDexuan Cui * performing the request by force. Linux always tries
2403e9c7205SDexuan Cui * to perform the request by force.
2413e9c7205SDexuan Cui */
24246a97191SGreg Kroah-Hartman switch (shutdown_msg->flags) {
24346a97191SGreg Kroah-Hartman case 0:
24446a97191SGreg Kroah-Hartman case 1:
24546a97191SGreg Kroah-Hartman icmsghdrp->status = HV_S_OK;
2463e9c7205SDexuan Cui work = &shutdown_work;
24706caa778SAndres Beltran pr_info("Shutdown request received - graceful shutdown initiated\n");
24846a97191SGreg Kroah-Hartman break;
2493e9c7205SDexuan Cui case 2:
2503e9c7205SDexuan Cui case 3:
2513e9c7205SDexuan Cui icmsghdrp->status = HV_S_OK;
2523e9c7205SDexuan Cui work = &restart_work;
25306caa778SAndres Beltran pr_info("Restart request received - graceful restart initiated\n");
2543e9c7205SDexuan Cui break;
255ffd1d4a4SDexuan Cui case 4:
256ffd1d4a4SDexuan Cui case 5:
257ffd1d4a4SDexuan Cui pr_info("Hibernation request received\n");
258ffd1d4a4SDexuan Cui icmsghdrp->status = hibernation_supported ?
259ffd1d4a4SDexuan Cui HV_S_OK : HV_E_FAIL;
260ffd1d4a4SDexuan Cui if (hibernation_supported)
261ffd1d4a4SDexuan Cui work = &hibernate_context.work;
262ffd1d4a4SDexuan Cui break;
26346a97191SGreg Kroah-Hartman default:
26446a97191SGreg Kroah-Hartman icmsghdrp->status = HV_E_FAIL;
26506caa778SAndres Beltran pr_info("Shutdown request received - Invalid request\n");
26646a97191SGreg Kroah-Hartman break;
26746a97191SGreg Kroah-Hartman }
26806caa778SAndres Beltran } else {
26906caa778SAndres Beltran icmsghdrp->status = HV_E_FAIL;
27006caa778SAndres Beltran pr_err_ratelimited("Shutdown request received. Invalid msg type: %d\n",
27106caa778SAndres Beltran icmsghdrp->icmsgtype);
27246a97191SGreg Kroah-Hartman }
27346a97191SGreg Kroah-Hartman
27446a97191SGreg Kroah-Hartman icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
27546a97191SGreg Kroah-Hartman | ICMSGHDRFLAG_RESPONSE;
27646a97191SGreg Kroah-Hartman
27746a97191SGreg Kroah-Hartman vmbus_sendpacket(channel, shut_txf_buf,
27846a97191SGreg Kroah-Hartman recvlen, requestid,
27946a97191SGreg Kroah-Hartman VM_PKT_DATA_INBAND, 0);
28046a97191SGreg Kroah-Hartman
2813e9c7205SDexuan Cui if (work)
2823e9c7205SDexuan Cui schedule_work(work);
28346a97191SGreg Kroah-Hartman }
28446a97191SGreg Kroah-Hartman
28546a97191SGreg Kroah-Hartman /*
28646a97191SGreg Kroah-Hartman * Set the host time in a process context.
28746a97191SGreg Kroah-Hartman */
2881d10602dSVitaly Kuznetsov static struct work_struct adj_time_work;
28946a97191SGreg Kroah-Hartman
2901d10602dSVitaly Kuznetsov /*
2911d10602dSVitaly Kuznetsov * The last time sample, received from the host. PTP device responds to
2921d10602dSVitaly Kuznetsov * requests by using this data and the current partition-wide time reference
2931d10602dSVitaly Kuznetsov * count.
2941d10602dSVitaly Kuznetsov */
2951d10602dSVitaly Kuznetsov static struct {
29646a97191SGreg Kroah-Hartman u64 host_time;
2978e1d2607SAlex Ng u64 ref_time;
2981d10602dSVitaly Kuznetsov spinlock_t lock;
2991d10602dSVitaly Kuznetsov } host_ts;
3001d10602dSVitaly Kuznetsov
reftime_to_ns(u64 reftime)30190b125f4SVineeth Pillai static inline u64 reftime_to_ns(u64 reftime)
3021d10602dSVitaly Kuznetsov {
30390b125f4SVineeth Pillai return (reftime - WLTIMEDELTA) * 100;
30490b125f4SVineeth Pillai }
30590b125f4SVineeth Pillai
30690b125f4SVineeth Pillai /*
30790b125f4SVineeth Pillai * Hard coded threshold for host timesync delay: 600 seconds
30890b125f4SVineeth Pillai */
30990b125f4SVineeth Pillai static const u64 HOST_TIMESYNC_DELAY_THRESH = 600 * (u64)NSEC_PER_SEC;
31090b125f4SVineeth Pillai
hv_get_adj_host_time(struct timespec64 * ts)31190b125f4SVineeth Pillai static int hv_get_adj_host_time(struct timespec64 *ts)
31290b125f4SVineeth Pillai {
31390b125f4SVineeth Pillai u64 newtime, reftime, timediff_adj;
3141d10602dSVitaly Kuznetsov unsigned long flags;
31590b125f4SVineeth Pillai int ret = 0;
3161d10602dSVitaly Kuznetsov
3171d10602dSVitaly Kuznetsov spin_lock_irqsave(&host_ts.lock, flags);
3180af3e137SAndrea Parri reftime = hv_read_reference_counter();
31990b125f4SVineeth Pillai
32090b125f4SVineeth Pillai /*
32190b125f4SVineeth Pillai * We need to let the caller know that last update from host
32290b125f4SVineeth Pillai * is older than the max allowable threshold. clock_gettime()
32390b125f4SVineeth Pillai * and PTP ioctl do not have a documented error that we could
32490b125f4SVineeth Pillai * return for this specific case. Use ESTALE to report this.
32590b125f4SVineeth Pillai */
32690b125f4SVineeth Pillai timediff_adj = reftime - host_ts.ref_time;
32790b125f4SVineeth Pillai if (timediff_adj * 100 > HOST_TIMESYNC_DELAY_THRESH) {
32890b125f4SVineeth Pillai pr_warn_once("TIMESYNC IC: Stale time stamp, %llu nsecs old\n",
32990b125f4SVineeth Pillai (timediff_adj * 100));
33090b125f4SVineeth Pillai ret = -ESTALE;
33190b125f4SVineeth Pillai }
33290b125f4SVineeth Pillai
33390b125f4SVineeth Pillai newtime = host_ts.host_time + timediff_adj;
33490b125f4SVineeth Pillai *ts = ns_to_timespec64(reftime_to_ns(newtime));
3351d10602dSVitaly Kuznetsov spin_unlock_irqrestore(&host_ts.lock, flags);
3361d10602dSVitaly Kuznetsov
33790b125f4SVineeth Pillai return ret;
3381d10602dSVitaly Kuznetsov }
33946a97191SGreg Kroah-Hartman
hv_set_host_time(struct work_struct * work)34046a97191SGreg Kroah-Hartman static void hv_set_host_time(struct work_struct *work)
34146a97191SGreg Kroah-Hartman {
34246a97191SGreg Kroah-Hartman
34390b125f4SVineeth Pillai struct timespec64 ts;
34490b125f4SVineeth Pillai
34590b125f4SVineeth Pillai if (!hv_get_adj_host_time(&ts))
3461d10602dSVitaly Kuznetsov do_settimeofday64(&ts);
34746a97191SGreg Kroah-Hartman }
34846a97191SGreg Kroah-Hartman
34946a97191SGreg Kroah-Hartman /*
35046a97191SGreg Kroah-Hartman * Synchronize time with host after reboot, restore, etc.
35146a97191SGreg Kroah-Hartman *
35246a97191SGreg Kroah-Hartman * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
35346a97191SGreg Kroah-Hartman * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
35446a97191SGreg Kroah-Hartman * message after the timesync channel is opened. Since the hv_utils module is
3552e338f7eSAlex Ng * loaded after hv_vmbus, the first message is usually missed. This bit is
3562e338f7eSAlex Ng * considered a hard request to discipline the clock.
3572e338f7eSAlex Ng *
3582e338f7eSAlex Ng * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is
3592e338f7eSAlex Ng * typically used as a hint to the guest. The guest is under no obligation
3602e338f7eSAlex Ng * to discipline the clock.
36146a97191SGreg Kroah-Hartman */
adj_guesttime(u64 hosttime,u64 reftime,u8 adj_flags)3623716a49aSVitaly Kuznetsov static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags)
36346a97191SGreg Kroah-Hartman {
3643716a49aSVitaly Kuznetsov unsigned long flags;
3653716a49aSVitaly Kuznetsov u64 cur_reftime;
36646a97191SGreg Kroah-Hartman
3673ba1eb17SVivek yadav /*
3683716a49aSVitaly Kuznetsov * Save the adjusted time sample from the host and the snapshot
3691d10602dSVitaly Kuznetsov * of the current system time.
3703716a49aSVitaly Kuznetsov */
3713716a49aSVitaly Kuznetsov spin_lock_irqsave(&host_ts.lock, flags);
3723716a49aSVitaly Kuznetsov
3730af3e137SAndrea Parri cur_reftime = hv_read_reference_counter();
3743716a49aSVitaly Kuznetsov host_ts.host_time = hosttime;
3753716a49aSVitaly Kuznetsov host_ts.ref_time = cur_reftime;
3763716a49aSVitaly Kuznetsov
3773716a49aSVitaly Kuznetsov /*
3783716a49aSVitaly Kuznetsov * TimeSync v4 messages contain reference time (guest's Hyper-V
3793716a49aSVitaly Kuznetsov * clocksource read when the time sample was generated), we can
3803716a49aSVitaly Kuznetsov * improve the precision by adding the delta between now and the
3811d10602dSVitaly Kuznetsov * time of generation. For older protocols we set
3821d10602dSVitaly Kuznetsov * reftime == cur_reftime on call.
3833716a49aSVitaly Kuznetsov */
3843716a49aSVitaly Kuznetsov host_ts.host_time += (cur_reftime - reftime);
3853716a49aSVitaly Kuznetsov
3863716a49aSVitaly Kuznetsov spin_unlock_irqrestore(&host_ts.lock, flags);
3871d10602dSVitaly Kuznetsov
3881d10602dSVitaly Kuznetsov /* Schedule work to do do_settimeofday64() */
3891d10602dSVitaly Kuznetsov if (adj_flags & ICTIMESYNCFLAG_SYNC)
3901d10602dSVitaly Kuznetsov schedule_work(&adj_time_work);
39146a97191SGreg Kroah-Hartman }
39246a97191SGreg Kroah-Hartman
39346a97191SGreg Kroah-Hartman /*
39446a97191SGreg Kroah-Hartman * Time Sync Channel message handler.
39546a97191SGreg Kroah-Hartman */
timesync_onchannelcallback(void * context)39646a97191SGreg Kroah-Hartman static void timesync_onchannelcallback(void *context)
39746a97191SGreg Kroah-Hartman {
39846a97191SGreg Kroah-Hartman struct vmbus_channel *channel = context;
39946a97191SGreg Kroah-Hartman u32 recvlen;
40046a97191SGreg Kroah-Hartman u64 requestid;
40146a97191SGreg Kroah-Hartman struct icmsg_hdr *icmsghdrp;
40246a97191SGreg Kroah-Hartman struct ictimesync_data *timedatap;
4038e1d2607SAlex Ng struct ictimesync_ref_data *refdata;
40446a97191SGreg Kroah-Hartman u8 *time_txf_buf = util_timesynch.recv_buffer;
40546a97191SGreg Kroah-Hartman
406b46b4a8aSVineeth Pillai /*
407b46b4a8aSVineeth Pillai * Drain the ring buffer and use the last packet to update
408b46b4a8aSVineeth Pillai * host_ts
409b46b4a8aSVineeth Pillai */
410b46b4a8aSVineeth Pillai while (1) {
411b46b4a8aSVineeth Pillai int ret = vmbus_recvpacket(channel, time_txf_buf,
412b46b4a8aSVineeth Pillai HV_HYP_PAGE_SIZE, &recvlen,
413b46b4a8aSVineeth Pillai &requestid);
414b46b4a8aSVineeth Pillai if (ret) {
41506caa778SAndres Beltran pr_err_ratelimited("TimeSync IC pkt recv failed (Err: %d)\n",
416b46b4a8aSVineeth Pillai ret);
417b46b4a8aSVineeth Pillai break;
418b46b4a8aSVineeth Pillai }
41946a97191SGreg Kroah-Hartman
420b46b4a8aSVineeth Pillai if (!recvlen)
421b46b4a8aSVineeth Pillai break;
422b46b4a8aSVineeth Pillai
42306caa778SAndres Beltran /* Ensure recvlen is big enough to read header data */
42406caa778SAndres Beltran if (recvlen < ICMSG_HDR) {
42506caa778SAndres Beltran pr_err_ratelimited("Timesync request received. Packet length too small: %d\n",
42606caa778SAndres Beltran recvlen);
42706caa778SAndres Beltran break;
42806caa778SAndres Beltran }
42906caa778SAndres Beltran
43046a97191SGreg Kroah-Hartman icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
43146a97191SGreg Kroah-Hartman sizeof(struct vmbuspipe_hdr)];
43246a97191SGreg Kroah-Hartman
43346a97191SGreg Kroah-Hartman if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
43406caa778SAndres Beltran if (vmbus_prep_negotiate_resp(icmsghdrp,
43506caa778SAndres Beltran time_txf_buf, recvlen,
436a1656454SAlex Ng fw_versions, FW_VER_COUNT,
437a1656454SAlex Ng ts_versions, TS_VER_COUNT,
438a1656454SAlex Ng NULL, &ts_srv_version)) {
4391274a690SAlex Ng pr_info("TimeSync IC version %d.%d\n",
440a1656454SAlex Ng ts_srv_version >> 16,
441a1656454SAlex Ng ts_srv_version & 0xFFFF);
442a1656454SAlex Ng }
44306caa778SAndres Beltran } else if (icmsghdrp->icmsgtype == ICMSGTYPE_TIMESYNC) {
4448e1d2607SAlex Ng if (ts_srv_version > TS_VERSION_3) {
44506caa778SAndres Beltran /* Ensure recvlen is big enough to read ictimesync_ref_data */
44606caa778SAndres Beltran if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_ref_data)) {
44706caa778SAndres Beltran pr_err_ratelimited("Invalid ictimesync ref data. Length too small: %u\n",
44806caa778SAndres Beltran recvlen);
44906caa778SAndres Beltran break;
45006caa778SAndres Beltran }
45106caa778SAndres Beltran refdata = (struct ictimesync_ref_data *)&time_txf_buf[ICMSG_HDR];
4528e1d2607SAlex Ng
4538e1d2607SAlex Ng adj_guesttime(refdata->parenttime,
4548e1d2607SAlex Ng refdata->vmreferencetime,
4558e1d2607SAlex Ng refdata->flags);
4568e1d2607SAlex Ng } else {
45706caa778SAndres Beltran /* Ensure recvlen is big enough to read ictimesync_data */
45806caa778SAndres Beltran if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_data)) {
45906caa778SAndres Beltran pr_err_ratelimited("Invalid ictimesync data. Length too small: %u\n",
46006caa778SAndres Beltran recvlen);
46106caa778SAndres Beltran break;
46206caa778SAndres Beltran }
46306caa778SAndres Beltran timedatap = (struct ictimesync_data *)&time_txf_buf[ICMSG_HDR];
46406caa778SAndres Beltran
4658e1d2607SAlex Ng adj_guesttime(timedatap->parenttime,
4660af3e137SAndrea Parri hv_read_reference_counter(),
4678e1d2607SAlex Ng timedatap->flags);
4688e1d2607SAlex Ng }
46906caa778SAndres Beltran } else {
47006caa778SAndres Beltran icmsghdrp->status = HV_E_FAIL;
47106caa778SAndres Beltran pr_err_ratelimited("Timesync request received. Invalid msg type: %d\n",
47206caa778SAndres Beltran icmsghdrp->icmsgtype);
47346a97191SGreg Kroah-Hartman }
47446a97191SGreg Kroah-Hartman
47546a97191SGreg Kroah-Hartman icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
47646a97191SGreg Kroah-Hartman | ICMSGHDRFLAG_RESPONSE;
47746a97191SGreg Kroah-Hartman
47846a97191SGreg Kroah-Hartman vmbus_sendpacket(channel, time_txf_buf,
47946a97191SGreg Kroah-Hartman recvlen, requestid,
48046a97191SGreg Kroah-Hartman VM_PKT_DATA_INBAND, 0);
48146a97191SGreg Kroah-Hartman }
48246a97191SGreg Kroah-Hartman }
48346a97191SGreg Kroah-Hartman
48446a97191SGreg Kroah-Hartman /*
48546a97191SGreg Kroah-Hartman * Heartbeat functionality.
48646a97191SGreg Kroah-Hartman * Every two seconds, Hyper-V send us a heartbeat request message.
48746a97191SGreg Kroah-Hartman * we respond to this message, and Hyper-V knows we are alive.
48846a97191SGreg Kroah-Hartman */
heartbeat_onchannelcallback(void * context)48946a97191SGreg Kroah-Hartman static void heartbeat_onchannelcallback(void *context)
49046a97191SGreg Kroah-Hartman {
49146a97191SGreg Kroah-Hartman struct vmbus_channel *channel = context;
49246a97191SGreg Kroah-Hartman u32 recvlen;
49346a97191SGreg Kroah-Hartman u64 requestid;
49446a97191SGreg Kroah-Hartman struct icmsg_hdr *icmsghdrp;
49546a97191SGreg Kroah-Hartman struct heartbeat_msg_data *heartbeat_msg;
49646a97191SGreg Kroah-Hartman u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
49746a97191SGreg Kroah-Hartman
498407a3aeeSLong Li while (1) {
499407a3aeeSLong Li
50006caa778SAndres Beltran if (vmbus_recvpacket(channel, hbeat_txf_buf, HV_HYP_PAGE_SIZE,
50106caa778SAndres Beltran &recvlen, &requestid)) {
50206caa778SAndres Beltran pr_err_ratelimited("Heartbeat request received. Could not read into hbeat txf buf\n");
50306caa778SAndres Beltran return;
50406caa778SAndres Beltran }
50546a97191SGreg Kroah-Hartman
506407a3aeeSLong Li if (!recvlen)
507407a3aeeSLong Li break;
508407a3aeeSLong Li
50906caa778SAndres Beltran /* Ensure recvlen is big enough to read header data */
51006caa778SAndres Beltran if (recvlen < ICMSG_HDR) {
511bdb49526SColin Ian King pr_err_ratelimited("Heartbeat request received. Packet length too small: %d\n",
51206caa778SAndres Beltran recvlen);
51306caa778SAndres Beltran break;
51406caa778SAndres Beltran }
51506caa778SAndres Beltran
51646a97191SGreg Kroah-Hartman icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
51746a97191SGreg Kroah-Hartman sizeof(struct vmbuspipe_hdr)];
51846a97191SGreg Kroah-Hartman
51946a97191SGreg Kroah-Hartman if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
520a1656454SAlex Ng if (vmbus_prep_negotiate_resp(icmsghdrp,
52106caa778SAndres Beltran hbeat_txf_buf, recvlen,
522a1656454SAlex Ng fw_versions, FW_VER_COUNT,
523a1656454SAlex Ng hb_versions, HB_VER_COUNT,
524a1656454SAlex Ng NULL, &hb_srv_version)) {
525a1656454SAlex Ng
5261274a690SAlex Ng pr_info("Heartbeat IC version %d.%d\n",
527a1656454SAlex Ng hb_srv_version >> 16,
528a1656454SAlex Ng hb_srv_version & 0xFFFF);
529a1656454SAlex Ng }
53006caa778SAndres Beltran } else if (icmsghdrp->icmsgtype == ICMSGTYPE_HEARTBEAT) {
53106caa778SAndres Beltran /*
53206caa778SAndres Beltran * Ensure recvlen is big enough to read seq_num. Reserved area is not
53306caa778SAndres Beltran * included in the check as the host may not fill it up entirely
53406caa778SAndres Beltran */
53506caa778SAndres Beltran if (recvlen < ICMSG_HDR + sizeof(u64)) {
53606caa778SAndres Beltran pr_err_ratelimited("Invalid heartbeat msg data. Length too small: %u\n",
53706caa778SAndres Beltran recvlen);
53806caa778SAndres Beltran break;
53906caa778SAndres Beltran }
54006caa778SAndres Beltran heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ICMSG_HDR];
54146a97191SGreg Kroah-Hartman
54246a97191SGreg Kroah-Hartman heartbeat_msg->seq_num += 1;
54306caa778SAndres Beltran } else {
54406caa778SAndres Beltran icmsghdrp->status = HV_E_FAIL;
54506caa778SAndres Beltran pr_err_ratelimited("Heartbeat request received. Invalid msg type: %d\n",
54606caa778SAndres Beltran icmsghdrp->icmsgtype);
54746a97191SGreg Kroah-Hartman }
54846a97191SGreg Kroah-Hartman
54946a97191SGreg Kroah-Hartman icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
55046a97191SGreg Kroah-Hartman | ICMSGHDRFLAG_RESPONSE;
55146a97191SGreg Kroah-Hartman
55246a97191SGreg Kroah-Hartman vmbus_sendpacket(channel, hbeat_txf_buf,
55346a97191SGreg Kroah-Hartman recvlen, requestid,
55446a97191SGreg Kroah-Hartman VM_PKT_DATA_INBAND, 0);
55546a97191SGreg Kroah-Hartman }
55646a97191SGreg Kroah-Hartman }
55746a97191SGreg Kroah-Hartman
558061dc93eSBoqun Feng #define HV_UTIL_RING_SEND_SIZE VMBUS_RING_SIZE(3 * HV_HYP_PAGE_SIZE)
559061dc93eSBoqun Feng #define HV_UTIL_RING_RECV_SIZE VMBUS_RING_SIZE(3 * HV_HYP_PAGE_SIZE)
560061dc93eSBoqun Feng
util_probe(struct hv_device * dev,const struct hv_vmbus_device_id * dev_id)56146a97191SGreg Kroah-Hartman static int util_probe(struct hv_device *dev,
56246a97191SGreg Kroah-Hartman const struct hv_vmbus_device_id *dev_id)
56346a97191SGreg Kroah-Hartman {
56446a97191SGreg Kroah-Hartman struct hv_util_service *srv =
56546a97191SGreg Kroah-Hartman (struct hv_util_service *)dev_id->driver_data;
56646a97191SGreg Kroah-Hartman int ret;
56746a97191SGreg Kroah-Hartman
568b14d749aSHimadri Pandya srv->recv_buffer = kmalloc(HV_HYP_PAGE_SIZE * 4, GFP_KERNEL);
56946a97191SGreg Kroah-Hartman if (!srv->recv_buffer)
57046a97191SGreg Kroah-Hartman return -ENOMEM;
571b9830d12SK. Y. Srinivasan srv->channel = dev->channel;
57246a97191SGreg Kroah-Hartman if (srv->util_init) {
57346a97191SGreg Kroah-Hartman ret = srv->util_init(srv);
57446a97191SGreg Kroah-Hartman if (ret) {
57546a97191SGreg Kroah-Hartman ret = -ENODEV;
57646a97191SGreg Kroah-Hartman goto error1;
57746a97191SGreg Kroah-Hartman }
57846a97191SGreg Kroah-Hartman }
57946a97191SGreg Kroah-Hartman
5807ae3e035SK. Y. Srinivasan /*
5817ae3e035SK. Y. Srinivasan * The set of services managed by the util driver are not performance
5827ae3e035SK. Y. Srinivasan * critical and do not need batched reading. Furthermore, some services
5837ae3e035SK. Y. Srinivasan * such as KVP can only handle one message from the host at a time.
5847ae3e035SK. Y. Srinivasan * Turn off batched reading for all util drivers before we open the
5857ae3e035SK. Y. Srinivasan * channel.
5867ae3e035SK. Y. Srinivasan */
587b71e3282SStephen Hemminger set_channel_read_mode(dev->channel, HV_CALL_DIRECT);
5887ae3e035SK. Y. Srinivasan
58946a97191SGreg Kroah-Hartman hv_set_drvdata(dev, srv);
59018965663SDexuan Cui
591061dc93eSBoqun Feng ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE,
592061dc93eSBoqun Feng HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb,
5930541a225SHimadri Pandya dev->channel);
59418965663SDexuan Cui if (ret)
59518965663SDexuan Cui goto error;
59618965663SDexuan Cui
597*89fcec5eSMichael Kelley if (srv->util_init_transport) {
598*89fcec5eSMichael Kelley ret = srv->util_init_transport();
599*89fcec5eSMichael Kelley if (ret) {
600*89fcec5eSMichael Kelley vmbus_close(dev->channel);
601*89fcec5eSMichael Kelley goto error;
602*89fcec5eSMichael Kelley }
603*89fcec5eSMichael Kelley }
60446a97191SGreg Kroah-Hartman return 0;
60546a97191SGreg Kroah-Hartman
60646a97191SGreg Kroah-Hartman error:
60746a97191SGreg Kroah-Hartman if (srv->util_deinit)
60846a97191SGreg Kroah-Hartman srv->util_deinit();
60946a97191SGreg Kroah-Hartman error1:
61046a97191SGreg Kroah-Hartman kfree(srv->recv_buffer);
61146a97191SGreg Kroah-Hartman return ret;
61246a97191SGreg Kroah-Hartman }
61346a97191SGreg Kroah-Hartman
util_remove(struct hv_device * dev)61496ec2939SDawei Li static void util_remove(struct hv_device *dev)
61546a97191SGreg Kroah-Hartman {
61646a97191SGreg Kroah-Hartman struct hv_util_service *srv = hv_get_drvdata(dev);
61746a97191SGreg Kroah-Hartman
61846a97191SGreg Kroah-Hartman if (srv->util_deinit)
61946a97191SGreg Kroah-Hartman srv->util_deinit();
6205380b383SK. Y. Srinivasan vmbus_close(dev->channel);
62146a97191SGreg Kroah-Hartman kfree(srv->recv_buffer);
62246a97191SGreg Kroah-Hartman }
62346a97191SGreg Kroah-Hartman
62454e19d34SDexuan Cui /*
62554e19d34SDexuan Cui * When we're in util_suspend(), all the userspace processes have been frozen
62654e19d34SDexuan Cui * (refer to hibernate() -> freeze_processes()). The userspace is thawed only
62754e19d34SDexuan Cui * after the whole resume procedure, including util_resume(), finishes.
62854e19d34SDexuan Cui */
util_suspend(struct hv_device * dev)62954e19d34SDexuan Cui static int util_suspend(struct hv_device *dev)
63054e19d34SDexuan Cui {
63154e19d34SDexuan Cui struct hv_util_service *srv = hv_get_drvdata(dev);
63254e19d34SDexuan Cui int ret = 0;
63354e19d34SDexuan Cui
63454e19d34SDexuan Cui if (srv->util_pre_suspend) {
63554e19d34SDexuan Cui ret = srv->util_pre_suspend();
63654e19d34SDexuan Cui if (ret)
63754e19d34SDexuan Cui return ret;
63854e19d34SDexuan Cui }
63954e19d34SDexuan Cui
64054e19d34SDexuan Cui vmbus_close(dev->channel);
64154e19d34SDexuan Cui
64254e19d34SDexuan Cui return 0;
64354e19d34SDexuan Cui }
64454e19d34SDexuan Cui
util_resume(struct hv_device * dev)64554e19d34SDexuan Cui static int util_resume(struct hv_device *dev)
64654e19d34SDexuan Cui {
64754e19d34SDexuan Cui struct hv_util_service *srv = hv_get_drvdata(dev);
64854e19d34SDexuan Cui int ret = 0;
64954e19d34SDexuan Cui
65054e19d34SDexuan Cui if (srv->util_pre_resume) {
65154e19d34SDexuan Cui ret = srv->util_pre_resume();
65254e19d34SDexuan Cui if (ret)
65354e19d34SDexuan Cui return ret;
65454e19d34SDexuan Cui }
65554e19d34SDexuan Cui
656061dc93eSBoqun Feng ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE,
657061dc93eSBoqun Feng HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb,
65854e19d34SDexuan Cui dev->channel);
65954e19d34SDexuan Cui return ret;
66054e19d34SDexuan Cui }
66154e19d34SDexuan Cui
66246a97191SGreg Kroah-Hartman static const struct hv_vmbus_device_id id_table[] = {
66346a97191SGreg Kroah-Hartman /* Shutdown guid */
664d13984e5SK. Y. Srinivasan { HV_SHUTDOWN_GUID,
665d13984e5SK. Y. Srinivasan .driver_data = (unsigned long)&util_shutdown
666d13984e5SK. Y. Srinivasan },
66746a97191SGreg Kroah-Hartman /* Time synch guid */
668d13984e5SK. Y. Srinivasan { HV_TS_GUID,
669d13984e5SK. Y. Srinivasan .driver_data = (unsigned long)&util_timesynch
670d13984e5SK. Y. Srinivasan },
67146a97191SGreg Kroah-Hartman /* Heartbeat guid */
672d13984e5SK. Y. Srinivasan { HV_HEART_BEAT_GUID,
673d13984e5SK. Y. Srinivasan .driver_data = (unsigned long)&util_heartbeat
674d13984e5SK. Y. Srinivasan },
67546a97191SGreg Kroah-Hartman /* KVP guid */
676d13984e5SK. Y. Srinivasan { HV_KVP_GUID,
677d13984e5SK. Y. Srinivasan .driver_data = (unsigned long)&util_kvp
678d13984e5SK. Y. Srinivasan },
67996dd86faSK. Y. Srinivasan /* VSS GUID */
68096dd86faSK. Y. Srinivasan { HV_VSS_GUID,
68196dd86faSK. Y. Srinivasan .driver_data = (unsigned long)&util_vss
68296dd86faSK. Y. Srinivasan },
68301325476SK. Y. Srinivasan /* File copy GUID */
68401325476SK. Y. Srinivasan { HV_FCOPY_GUID,
68501325476SK. Y. Srinivasan .driver_data = (unsigned long)&util_fcopy
68601325476SK. Y. Srinivasan },
68746a97191SGreg Kroah-Hartman { },
68846a97191SGreg Kroah-Hartman };
68946a97191SGreg Kroah-Hartman
69046a97191SGreg Kroah-Hartman MODULE_DEVICE_TABLE(vmbus, id_table);
69146a97191SGreg Kroah-Hartman
69246a97191SGreg Kroah-Hartman /* The one and only one */
69346a97191SGreg Kroah-Hartman static struct hv_driver util_drv = {
6945c24ee89SHaiyang Zhang .name = "hv_utils",
69546a97191SGreg Kroah-Hartman .id_table = id_table,
69646a97191SGreg Kroah-Hartman .probe = util_probe,
69746a97191SGreg Kroah-Hartman .remove = util_remove,
69854e19d34SDexuan Cui .suspend = util_suspend,
69954e19d34SDexuan Cui .resume = util_resume,
700af0a5646SArjan van de Ven .driver = {
701af0a5646SArjan van de Ven .probe_type = PROBE_PREFER_ASYNCHRONOUS,
702af0a5646SArjan van de Ven },
70346a97191SGreg Kroah-Hartman };
70446a97191SGreg Kroah-Hartman
hv_ptp_enable(struct ptp_clock_info * info,struct ptp_clock_request * request,int on)7053716a49aSVitaly Kuznetsov static int hv_ptp_enable(struct ptp_clock_info *info,
7063716a49aSVitaly Kuznetsov struct ptp_clock_request *request, int on)
7073716a49aSVitaly Kuznetsov {
7083716a49aSVitaly Kuznetsov return -EOPNOTSUPP;
7093716a49aSVitaly Kuznetsov }
7103716a49aSVitaly Kuznetsov
hv_ptp_settime(struct ptp_clock_info * p,const struct timespec64 * ts)7113716a49aSVitaly Kuznetsov static int hv_ptp_settime(struct ptp_clock_info *p, const struct timespec64 *ts)
7123716a49aSVitaly Kuznetsov {
7133716a49aSVitaly Kuznetsov return -EOPNOTSUPP;
7143716a49aSVitaly Kuznetsov }
7153716a49aSVitaly Kuznetsov
hv_ptp_adjfine(struct ptp_clock_info * ptp,long delta)71673aa29a2SJacob Keller static int hv_ptp_adjfine(struct ptp_clock_info *ptp, long delta)
7173716a49aSVitaly Kuznetsov {
7183716a49aSVitaly Kuznetsov return -EOPNOTSUPP;
7193716a49aSVitaly Kuznetsov }
hv_ptp_adjtime(struct ptp_clock_info * ptp,s64 delta)7203716a49aSVitaly Kuznetsov static int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
7213716a49aSVitaly Kuznetsov {
7223716a49aSVitaly Kuznetsov return -EOPNOTSUPP;
7233716a49aSVitaly Kuznetsov }
7243716a49aSVitaly Kuznetsov
hv_ptp_gettime(struct ptp_clock_info * info,struct timespec64 * ts)7253716a49aSVitaly Kuznetsov static int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts)
7263716a49aSVitaly Kuznetsov {
72790b125f4SVineeth Pillai return hv_get_adj_host_time(ts);
7283716a49aSVitaly Kuznetsov }
7293716a49aSVitaly Kuznetsov
7303716a49aSVitaly Kuznetsov static struct ptp_clock_info ptp_hyperv_info = {
7313716a49aSVitaly Kuznetsov .name = "hyperv",
7323716a49aSVitaly Kuznetsov .enable = hv_ptp_enable,
7333716a49aSVitaly Kuznetsov .adjtime = hv_ptp_adjtime,
73473aa29a2SJacob Keller .adjfine = hv_ptp_adjfine,
7353716a49aSVitaly Kuznetsov .gettime64 = hv_ptp_gettime,
7363716a49aSVitaly Kuznetsov .settime64 = hv_ptp_settime,
7373716a49aSVitaly Kuznetsov .owner = THIS_MODULE,
7383716a49aSVitaly Kuznetsov };
7393716a49aSVitaly Kuznetsov
7403716a49aSVitaly Kuznetsov static struct ptp_clock *hv_ptp_clock;
7413716a49aSVitaly Kuznetsov
hv_timesync_init(struct hv_util_service * srv)7423ba1eb17SVivek yadav static int hv_timesync_init(struct hv_util_service *srv)
7433ba1eb17SVivek yadav {
7445a16dfc8SDexuan Cui spin_lock_init(&host_ts.lock);
7455a16dfc8SDexuan Cui
7461d10602dSVitaly Kuznetsov INIT_WORK(&adj_time_work, hv_set_host_time);
7473716a49aSVitaly Kuznetsov
7483716a49aSVitaly Kuznetsov /*
7493716a49aSVitaly Kuznetsov * ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is
7503716a49aSVitaly Kuznetsov * disabled but the driver is still useful without the PTP device
7513716a49aSVitaly Kuznetsov * as it still handles the ICTIMESYNCFLAG_SYNC case.
7523716a49aSVitaly Kuznetsov */
7533716a49aSVitaly Kuznetsov hv_ptp_clock = ptp_clock_register(&ptp_hyperv_info, NULL);
7543716a49aSVitaly Kuznetsov if (IS_ERR_OR_NULL(hv_ptp_clock)) {
755c6a8625fSYueHaibing pr_err("cannot register PTP clock: %d\n",
756c6a8625fSYueHaibing PTR_ERR_OR_ZERO(hv_ptp_clock));
7573716a49aSVitaly Kuznetsov hv_ptp_clock = NULL;
7583716a49aSVitaly Kuznetsov }
7593716a49aSVitaly Kuznetsov
7603ba1eb17SVivek yadav return 0;
7613ba1eb17SVivek yadav }
7623ba1eb17SVivek yadav
hv_timesync_cancel_work(void)76354e19d34SDexuan Cui static void hv_timesync_cancel_work(void)
76454e19d34SDexuan Cui {
76554e19d34SDexuan Cui cancel_work_sync(&adj_time_work);
76654e19d34SDexuan Cui }
76754e19d34SDexuan Cui
hv_timesync_pre_suspend(void)76854e19d34SDexuan Cui static int hv_timesync_pre_suspend(void)
76954e19d34SDexuan Cui {
77054e19d34SDexuan Cui hv_timesync_cancel_work();
77154e19d34SDexuan Cui return 0;
77254e19d34SDexuan Cui }
77354e19d34SDexuan Cui
hv_timesync_deinit(void)7743ba1eb17SVivek yadav static void hv_timesync_deinit(void)
7753ba1eb17SVivek yadav {
7763716a49aSVitaly Kuznetsov if (hv_ptp_clock)
7773716a49aSVitaly Kuznetsov ptp_clock_unregister(hv_ptp_clock);
77854e19d34SDexuan Cui
77954e19d34SDexuan Cui hv_timesync_cancel_work();
7803ba1eb17SVivek yadav }
7813ba1eb17SVivek yadav
init_hyperv_utils(void)78246a97191SGreg Kroah-Hartman static int __init init_hyperv_utils(void)
78346a97191SGreg Kroah-Hartman {
78446a97191SGreg Kroah-Hartman pr_info("Registering HyperV Utility Driver\n");
78546a97191SGreg Kroah-Hartman
78646a97191SGreg Kroah-Hartman return vmbus_driver_register(&util_drv);
78746a97191SGreg Kroah-Hartman }
78846a97191SGreg Kroah-Hartman
exit_hyperv_utils(void)78946a97191SGreg Kroah-Hartman static void exit_hyperv_utils(void)
79046a97191SGreg Kroah-Hartman {
79146a97191SGreg Kroah-Hartman pr_info("De-Registered HyperV Utility Driver\n");
79246a97191SGreg Kroah-Hartman
79346a97191SGreg Kroah-Hartman vmbus_driver_unregister(&util_drv);
79446a97191SGreg Kroah-Hartman }
79546a97191SGreg Kroah-Hartman
79646a97191SGreg Kroah-Hartman module_init(init_hyperv_utils);
79746a97191SGreg Kroah-Hartman module_exit(exit_hyperv_utils);
79846a97191SGreg Kroah-Hartman
79946a97191SGreg Kroah-Hartman MODULE_DESCRIPTION("Hyper-V Utilities");
80046a97191SGreg Kroah-Hartman MODULE_LICENSE("GPL");
801