linux/drivers/hv/hv_util.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2010, Microsoft Corporation.
   4 *
   5 * Authors:
   6 *   Haiyang Zhang <haiyangz@microsoft.com>
   7 *   Hank Janssen  <hjanssen@microsoft.com>
   8 */
   9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  10
  11#include <linux/kernel.h>
  12#include <linux/init.h>
  13#include <linux/module.h>
  14#include <linux/slab.h>
  15#include <linux/sysctl.h>
  16#include <linux/reboot.h>
  17#include <linux/hyperv.h>
  18#include <linux/clockchips.h>
  19#include <linux/ptp_clock_kernel.h>
  20#include <clocksource/hyperv_timer.h>
  21#include <asm/mshyperv.h>
  22
  23#include "hyperv_vmbus.h"
  24
  25#define SD_MAJOR        3
  26#define SD_MINOR        0
  27#define SD_VERSION      (SD_MAJOR << 16 | SD_MINOR)
  28
  29#define SD_MAJOR_1      1
  30#define SD_VERSION_1    (SD_MAJOR_1 << 16 | SD_MINOR)
  31
  32#define TS_MAJOR        4
  33#define TS_MINOR        0
  34#define TS_VERSION      (TS_MAJOR << 16 | TS_MINOR)
  35
  36#define TS_MAJOR_1      1
  37#define TS_VERSION_1    (TS_MAJOR_1 << 16 | TS_MINOR)
  38
  39#define TS_MAJOR_3      3
  40#define TS_VERSION_3    (TS_MAJOR_3 << 16 | TS_MINOR)
  41
  42#define HB_MAJOR        3
  43#define HB_MINOR        0
  44#define HB_VERSION      (HB_MAJOR << 16 | HB_MINOR)
  45
  46#define HB_MAJOR_1      1
  47#define HB_VERSION_1    (HB_MAJOR_1 << 16 | HB_MINOR)
  48
  49static int sd_srv_version;
  50static int ts_srv_version;
  51static int hb_srv_version;
  52
  53#define SD_VER_COUNT 2
  54static const int sd_versions[] = {
  55        SD_VERSION,
  56        SD_VERSION_1
  57};
  58
  59#define TS_VER_COUNT 3
  60static const int ts_versions[] = {
  61        TS_VERSION,
  62        TS_VERSION_3,
  63        TS_VERSION_1
  64};
  65
  66#define HB_VER_COUNT 2
  67static const int hb_versions[] = {
  68        HB_VERSION,
  69        HB_VERSION_1
  70};
  71
  72#define FW_VER_COUNT 2
  73static const int fw_versions[] = {
  74        UTIL_FW_VERSION,
  75        UTIL_WS2K8_FW_VERSION
  76};
  77
  78static void shutdown_onchannelcallback(void *context);
  79static struct hv_util_service util_shutdown = {
  80        .util_cb = shutdown_onchannelcallback,
  81};
  82
  83static int hv_timesync_init(struct hv_util_service *srv);
  84static void hv_timesync_deinit(void);
  85
  86static void timesync_onchannelcallback(void *context);
  87static struct hv_util_service util_timesynch = {
  88        .util_cb = timesync_onchannelcallback,
  89        .util_init = hv_timesync_init,
  90        .util_deinit = hv_timesync_deinit,
  91};
  92
  93static void heartbeat_onchannelcallback(void *context);
  94static struct hv_util_service util_heartbeat = {
  95        .util_cb = heartbeat_onchannelcallback,
  96};
  97
  98static struct hv_util_service util_kvp = {
  99        .util_cb = hv_kvp_onchannelcallback,
 100        .util_init = hv_kvp_init,
 101        .util_deinit = hv_kvp_deinit,
 102};
 103
 104static struct hv_util_service util_vss = {
 105        .util_cb = hv_vss_onchannelcallback,
 106        .util_init = hv_vss_init,
 107        .util_deinit = hv_vss_deinit,
 108};
 109
 110static struct hv_util_service util_fcopy = {
 111        .util_cb = hv_fcopy_onchannelcallback,
 112        .util_init = hv_fcopy_init,
 113        .util_deinit = hv_fcopy_deinit,
 114};
 115
 116static void perform_shutdown(struct work_struct *dummy)
 117{
 118        orderly_poweroff(true);
 119}
 120
 121/*
 122 * Perform the shutdown operation in a thread context.
 123 */
 124static DECLARE_WORK(shutdown_work, perform_shutdown);
 125
 126static void shutdown_onchannelcallback(void *context)
 127{
 128        struct vmbus_channel *channel = context;
 129        u32 recvlen;
 130        u64 requestid;
 131        bool execute_shutdown = false;
 132        u8  *shut_txf_buf = util_shutdown.recv_buffer;
 133
 134        struct shutdown_msg_data *shutdown_msg;
 135
 136        struct icmsg_hdr *icmsghdrp;
 137
 138        vmbus_recvpacket(channel, shut_txf_buf,
 139                         PAGE_SIZE, &recvlen, &requestid);
 140
 141        if (recvlen > 0) {
 142                icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[
 143                        sizeof(struct vmbuspipe_hdr)];
 144
 145                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
 146                        if (vmbus_prep_negotiate_resp(icmsghdrp, shut_txf_buf,
 147                                        fw_versions, FW_VER_COUNT,
 148                                        sd_versions, SD_VER_COUNT,
 149                                        NULL, &sd_srv_version)) {
 150                                pr_info("Shutdown IC version %d.%d\n",
 151                                        sd_srv_version >> 16,
 152                                        sd_srv_version & 0xFFFF);
 153                        }
 154                } else {
 155                        shutdown_msg =
 156                                (struct shutdown_msg_data *)&shut_txf_buf[
 157                                        sizeof(struct vmbuspipe_hdr) +
 158                                        sizeof(struct icmsg_hdr)];
 159
 160                        switch (shutdown_msg->flags) {
 161                        case 0:
 162                        case 1:
 163                                icmsghdrp->status = HV_S_OK;
 164                                execute_shutdown = true;
 165
 166                                pr_info("Shutdown request received -"
 167                                            " graceful shutdown initiated\n");
 168                                break;
 169                        default:
 170                                icmsghdrp->status = HV_E_FAIL;
 171                                execute_shutdown = false;
 172
 173                                pr_info("Shutdown request received -"
 174                                            " Invalid request\n");
 175                                break;
 176                        }
 177                }
 178
 179                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
 180                        | ICMSGHDRFLAG_RESPONSE;
 181
 182                vmbus_sendpacket(channel, shut_txf_buf,
 183                                       recvlen, requestid,
 184                                       VM_PKT_DATA_INBAND, 0);
 185        }
 186
 187        if (execute_shutdown == true)
 188                schedule_work(&shutdown_work);
 189}
 190
 191/*
 192 * Set the host time in a process context.
 193 */
 194static struct work_struct adj_time_work;
 195
 196/*
 197 * The last time sample, received from the host. PTP device responds to
 198 * requests by using this data and the current partition-wide time reference
 199 * count.
 200 */
 201static struct {
 202        u64                             host_time;
 203        u64                             ref_time;
 204        spinlock_t                      lock;
 205} host_ts;
 206
 207static struct timespec64 hv_get_adj_host_time(void)
 208{
 209        struct timespec64 ts;
 210        u64 newtime, reftime;
 211        unsigned long flags;
 212
 213        spin_lock_irqsave(&host_ts.lock, flags);
 214        reftime = hyperv_cs->read(hyperv_cs);
 215        newtime = host_ts.host_time + (reftime - host_ts.ref_time);
 216        ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100);
 217        spin_unlock_irqrestore(&host_ts.lock, flags);
 218
 219        return ts;
 220}
 221
 222static void hv_set_host_time(struct work_struct *work)
 223{
 224        struct timespec64 ts = hv_get_adj_host_time();
 225
 226        do_settimeofday64(&ts);
 227}
 228
 229/*
 230 * Synchronize time with host after reboot, restore, etc.
 231 *
 232 * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
 233 * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
 234 * message after the timesync channel is opened. Since the hv_utils module is
 235 * loaded after hv_vmbus, the first message is usually missed. This bit is
 236 * considered a hard request to discipline the clock.
 237 *
 238 * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is
 239 * typically used as a hint to the guest. The guest is under no obligation
 240 * to discipline the clock.
 241 */
 242static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags)
 243{
 244        unsigned long flags;
 245        u64 cur_reftime;
 246
 247        /*
 248         * Save the adjusted time sample from the host and the snapshot
 249         * of the current system time.
 250         */
 251        spin_lock_irqsave(&host_ts.lock, flags);
 252
 253        cur_reftime = hyperv_cs->read(hyperv_cs);
 254        host_ts.host_time = hosttime;
 255        host_ts.ref_time = cur_reftime;
 256
 257        /*
 258         * TimeSync v4 messages contain reference time (guest's Hyper-V
 259         * clocksource read when the time sample was generated), we can
 260         * improve the precision by adding the delta between now and the
 261         * time of generation. For older protocols we set
 262         * reftime == cur_reftime on call.
 263         */
 264        host_ts.host_time += (cur_reftime - reftime);
 265
 266        spin_unlock_irqrestore(&host_ts.lock, flags);
 267
 268        /* Schedule work to do do_settimeofday64() */
 269        if (adj_flags & ICTIMESYNCFLAG_SYNC)
 270                schedule_work(&adj_time_work);
 271}
 272
 273/*
 274 * Time Sync Channel message handler.
 275 */
 276static void timesync_onchannelcallback(void *context)
 277{
 278        struct vmbus_channel *channel = context;
 279        u32 recvlen;
 280        u64 requestid;
 281        struct icmsg_hdr *icmsghdrp;
 282        struct ictimesync_data *timedatap;
 283        struct ictimesync_ref_data *refdata;
 284        u8 *time_txf_buf = util_timesynch.recv_buffer;
 285
 286        vmbus_recvpacket(channel, time_txf_buf,
 287                         PAGE_SIZE, &recvlen, &requestid);
 288
 289        if (recvlen > 0) {
 290                icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
 291                                sizeof(struct vmbuspipe_hdr)];
 292
 293                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
 294                        if (vmbus_prep_negotiate_resp(icmsghdrp, time_txf_buf,
 295                                                fw_versions, FW_VER_COUNT,
 296                                                ts_versions, TS_VER_COUNT,
 297                                                NULL, &ts_srv_version)) {
 298                                pr_info("TimeSync IC version %d.%d\n",
 299                                        ts_srv_version >> 16,
 300                                        ts_srv_version & 0xFFFF);
 301                        }
 302                } else {
 303                        if (ts_srv_version > TS_VERSION_3) {
 304                                refdata = (struct ictimesync_ref_data *)
 305                                        &time_txf_buf[
 306                                        sizeof(struct vmbuspipe_hdr) +
 307                                        sizeof(struct icmsg_hdr)];
 308
 309                                adj_guesttime(refdata->parenttime,
 310                                                refdata->vmreferencetime,
 311                                                refdata->flags);
 312                        } else {
 313                                timedatap = (struct ictimesync_data *)
 314                                        &time_txf_buf[
 315                                        sizeof(struct vmbuspipe_hdr) +
 316                                        sizeof(struct icmsg_hdr)];
 317                                adj_guesttime(timedatap->parenttime,
 318                                              hyperv_cs->read(hyperv_cs),
 319                                              timedatap->flags);
 320                        }
 321                }
 322
 323                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
 324                        | ICMSGHDRFLAG_RESPONSE;
 325
 326                vmbus_sendpacket(channel, time_txf_buf,
 327                                recvlen, requestid,
 328                                VM_PKT_DATA_INBAND, 0);
 329        }
 330}
 331
 332/*
 333 * Heartbeat functionality.
 334 * Every two seconds, Hyper-V send us a heartbeat request message.
 335 * we respond to this message, and Hyper-V knows we are alive.
 336 */
 337static void heartbeat_onchannelcallback(void *context)
 338{
 339        struct vmbus_channel *channel = context;
 340        u32 recvlen;
 341        u64 requestid;
 342        struct icmsg_hdr *icmsghdrp;
 343        struct heartbeat_msg_data *heartbeat_msg;
 344        u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
 345
 346        while (1) {
 347
 348                vmbus_recvpacket(channel, hbeat_txf_buf,
 349                                 PAGE_SIZE, &recvlen, &requestid);
 350
 351                if (!recvlen)
 352                        break;
 353
 354                icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
 355                                sizeof(struct vmbuspipe_hdr)];
 356
 357                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
 358                        if (vmbus_prep_negotiate_resp(icmsghdrp,
 359                                        hbeat_txf_buf,
 360                                        fw_versions, FW_VER_COUNT,
 361                                        hb_versions, HB_VER_COUNT,
 362                                        NULL, &hb_srv_version)) {
 363
 364                                pr_info("Heartbeat IC version %d.%d\n",
 365                                        hb_srv_version >> 16,
 366                                        hb_srv_version & 0xFFFF);
 367                        }
 368                } else {
 369                        heartbeat_msg =
 370                                (struct heartbeat_msg_data *)&hbeat_txf_buf[
 371                                        sizeof(struct vmbuspipe_hdr) +
 372                                        sizeof(struct icmsg_hdr)];
 373
 374                        heartbeat_msg->seq_num += 1;
 375                }
 376
 377                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
 378                        | ICMSGHDRFLAG_RESPONSE;
 379
 380                vmbus_sendpacket(channel, hbeat_txf_buf,
 381                                       recvlen, requestid,
 382                                       VM_PKT_DATA_INBAND, 0);
 383        }
 384}
 385
 386static int util_probe(struct hv_device *dev,
 387                        const struct hv_vmbus_device_id *dev_id)
 388{
 389        struct hv_util_service *srv =
 390                (struct hv_util_service *)dev_id->driver_data;
 391        int ret;
 392
 393        srv->recv_buffer = kmalloc(PAGE_SIZE * 4, GFP_KERNEL);
 394        if (!srv->recv_buffer)
 395                return -ENOMEM;
 396        srv->channel = dev->channel;
 397        if (srv->util_init) {
 398                ret = srv->util_init(srv);
 399                if (ret) {
 400                        ret = -ENODEV;
 401                        goto error1;
 402                }
 403        }
 404
 405        /*
 406         * The set of services managed by the util driver are not performance
 407         * critical and do not need batched reading. Furthermore, some services
 408         * such as KVP can only handle one message from the host at a time.
 409         * Turn off batched reading for all util drivers before we open the
 410         * channel.
 411         */
 412        set_channel_read_mode(dev->channel, HV_CALL_DIRECT);
 413
 414        hv_set_drvdata(dev, srv);
 415
 416        ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
 417                        srv->util_cb, dev->channel);
 418        if (ret)
 419                goto error;
 420
 421        return 0;
 422
 423error:
 424        if (srv->util_deinit)
 425                srv->util_deinit();
 426error1:
 427        kfree(srv->recv_buffer);
 428        return ret;
 429}
 430
 431static int util_remove(struct hv_device *dev)
 432{
 433        struct hv_util_service *srv = hv_get_drvdata(dev);
 434
 435        if (srv->util_deinit)
 436                srv->util_deinit();
 437        vmbus_close(dev->channel);
 438        kfree(srv->recv_buffer);
 439
 440        return 0;
 441}
 442
 443static const struct hv_vmbus_device_id id_table[] = {
 444        /* Shutdown guid */
 445        { HV_SHUTDOWN_GUID,
 446          .driver_data = (unsigned long)&util_shutdown
 447        },
 448        /* Time synch guid */
 449        { HV_TS_GUID,
 450          .driver_data = (unsigned long)&util_timesynch
 451        },
 452        /* Heartbeat guid */
 453        { HV_HEART_BEAT_GUID,
 454          .driver_data = (unsigned long)&util_heartbeat
 455        },
 456        /* KVP guid */
 457        { HV_KVP_GUID,
 458          .driver_data = (unsigned long)&util_kvp
 459        },
 460        /* VSS GUID */
 461        { HV_VSS_GUID,
 462          .driver_data = (unsigned long)&util_vss
 463        },
 464        /* File copy GUID */
 465        { HV_FCOPY_GUID,
 466          .driver_data = (unsigned long)&util_fcopy
 467        },
 468        { },
 469};
 470
 471MODULE_DEVICE_TABLE(vmbus, id_table);
 472
 473/* The one and only one */
 474static  struct hv_driver util_drv = {
 475        .name = "hv_utils",
 476        .id_table = id_table,
 477        .probe =  util_probe,
 478        .remove =  util_remove,
 479        .driver = {
 480                .probe_type = PROBE_PREFER_ASYNCHRONOUS,
 481        },
 482};
 483
 484static int hv_ptp_enable(struct ptp_clock_info *info,
 485                         struct ptp_clock_request *request, int on)
 486{
 487        return -EOPNOTSUPP;
 488}
 489
 490static int hv_ptp_settime(struct ptp_clock_info *p, const struct timespec64 *ts)
 491{
 492        return -EOPNOTSUPP;
 493}
 494
 495static int hv_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta)
 496{
 497        return -EOPNOTSUPP;
 498}
 499static int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
 500{
 501        return -EOPNOTSUPP;
 502}
 503
 504static int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts)
 505{
 506        *ts = hv_get_adj_host_time();
 507
 508        return 0;
 509}
 510
 511static struct ptp_clock_info ptp_hyperv_info = {
 512        .name           = "hyperv",
 513        .enable         = hv_ptp_enable,
 514        .adjtime        = hv_ptp_adjtime,
 515        .adjfreq        = hv_ptp_adjfreq,
 516        .gettime64      = hv_ptp_gettime,
 517        .settime64      = hv_ptp_settime,
 518        .owner          = THIS_MODULE,
 519};
 520
 521static struct ptp_clock *hv_ptp_clock;
 522
 523static int hv_timesync_init(struct hv_util_service *srv)
 524{
 525        /* TimeSync requires Hyper-V clocksource. */
 526        if (!hyperv_cs)
 527                return -ENODEV;
 528
 529        spin_lock_init(&host_ts.lock);
 530
 531        INIT_WORK(&adj_time_work, hv_set_host_time);
 532
 533        /*
 534         * ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is
 535         * disabled but the driver is still useful without the PTP device
 536         * as it still handles the ICTIMESYNCFLAG_SYNC case.
 537         */
 538        hv_ptp_clock = ptp_clock_register(&ptp_hyperv_info, NULL);
 539        if (IS_ERR_OR_NULL(hv_ptp_clock)) {
 540                pr_err("cannot register PTP clock: %ld\n",
 541                       PTR_ERR(hv_ptp_clock));
 542                hv_ptp_clock = NULL;
 543        }
 544
 545        return 0;
 546}
 547
 548static void hv_timesync_deinit(void)
 549{
 550        if (hv_ptp_clock)
 551                ptp_clock_unregister(hv_ptp_clock);
 552        cancel_work_sync(&adj_time_work);
 553}
 554
 555static int __init init_hyperv_utils(void)
 556{
 557        pr_info("Registering HyperV Utility Driver\n");
 558
 559        return vmbus_driver_register(&util_drv);
 560}
 561
 562static void exit_hyperv_utils(void)
 563{
 564        pr_info("De-Registered HyperV Utility Driver\n");
 565
 566        vmbus_driver_unregister(&util_drv);
 567}
 568
 569module_init(init_hyperv_utils);
 570module_exit(exit_hyperv_utils);
 571
 572MODULE_DESCRIPTION("Hyper-V Utilities");
 573MODULE_LICENSE("GPL");
 574