linux/drivers/hv/hv_util.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010, Microsoft Corporation.
   3 *
   4 * This program is free software; you can redistribute it and/or modify it
   5 * under the terms and conditions of the GNU General Public License,
   6 * version 2, as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope it will be useful, but WITHOUT
   9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  11 * more details.
  12 *
  13 * You should have received a copy of the GNU General Public License along with
  14 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  15 * Place - Suite 330, Boston, MA 02111-1307 USA.
  16 *
  17 * Authors:
  18 *   Haiyang Zhang <haiyangz@microsoft.com>
  19 *   Hank Janssen  <hjanssen@microsoft.com>
  20 */
  21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  22
  23#include <linux/kernel.h>
  24#include <linux/init.h>
  25#include <linux/module.h>
  26#include <linux/slab.h>
  27#include <linux/sysctl.h>
  28#include <linux/reboot.h>
  29#include <linux/hyperv.h>
  30
  31#include "hyperv_vmbus.h"
  32
  33#define SD_MAJOR        3
  34#define SD_MINOR        0
  35#define SD_VERSION      (SD_MAJOR << 16 | SD_MINOR)
  36
  37#define SD_WS2008_MAJOR         1
  38#define SD_WS2008_VERSION       (SD_WS2008_MAJOR << 16 | SD_MINOR)
  39
  40#define TS_MAJOR        3
  41#define TS_MINOR        0
  42#define TS_VERSION      (TS_MAJOR << 16 | TS_MINOR)
  43
  44#define TS_WS2008_MAJOR         1
  45#define TS_WS2008_VERSION       (TS_WS2008_MAJOR << 16 | TS_MINOR)
  46
  47#define HB_MAJOR        3
  48#define HB_MINOR 0
  49#define HB_VERSION      (HB_MAJOR << 16 | HB_MINOR)
  50
  51#define HB_WS2008_MAJOR 1
  52#define HB_WS2008_VERSION       (HB_WS2008_MAJOR << 16 | HB_MINOR)
  53
  54static int sd_srv_version;
  55static int ts_srv_version;
  56static int hb_srv_version;
  57static int util_fw_version;
  58
  59static void shutdown_onchannelcallback(void *context);
  60static struct hv_util_service util_shutdown = {
  61        .util_cb = shutdown_onchannelcallback,
  62};
  63
  64static void timesync_onchannelcallback(void *context);
  65static struct hv_util_service util_timesynch = {
  66        .util_cb = timesync_onchannelcallback,
  67};
  68
  69static void heartbeat_onchannelcallback(void *context);
  70static struct hv_util_service util_heartbeat = {
  71        .util_cb = heartbeat_onchannelcallback,
  72};
  73
  74static struct hv_util_service util_kvp = {
  75        .util_cb = hv_kvp_onchannelcallback,
  76        .util_init = hv_kvp_init,
  77        .util_deinit = hv_kvp_deinit,
  78};
  79
  80static struct hv_util_service util_vss = {
  81        .util_cb = hv_vss_onchannelcallback,
  82        .util_init = hv_vss_init,
  83        .util_deinit = hv_vss_deinit,
  84};
  85
  86static struct hv_util_service util_fcopy = {
  87        .util_cb = hv_fcopy_onchannelcallback,
  88        .util_init = hv_fcopy_init,
  89        .util_deinit = hv_fcopy_deinit,
  90};
  91
  92static void perform_shutdown(struct work_struct *dummy)
  93{
  94        orderly_poweroff(true);
  95}
  96
  97/*
  98 * Perform the shutdown operation in a thread context.
  99 */
 100static DECLARE_WORK(shutdown_work, perform_shutdown);
 101
 102static void shutdown_onchannelcallback(void *context)
 103{
 104        struct vmbus_channel *channel = context;
 105        u32 recvlen;
 106        u64 requestid;
 107        bool execute_shutdown = false;
 108        u8  *shut_txf_buf = util_shutdown.recv_buffer;
 109
 110        struct shutdown_msg_data *shutdown_msg;
 111
 112        struct icmsg_hdr *icmsghdrp;
 113        struct icmsg_negotiate *negop = NULL;
 114
 115        vmbus_recvpacket(channel, shut_txf_buf,
 116                         PAGE_SIZE, &recvlen, &requestid);
 117
 118        if (recvlen > 0) {
 119                icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[
 120                        sizeof(struct vmbuspipe_hdr)];
 121
 122                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
 123                        vmbus_prep_negotiate_resp(icmsghdrp, negop,
 124                                        shut_txf_buf, util_fw_version,
 125                                        sd_srv_version);
 126                } else {
 127                        shutdown_msg =
 128                                (struct shutdown_msg_data *)&shut_txf_buf[
 129                                        sizeof(struct vmbuspipe_hdr) +
 130                                        sizeof(struct icmsg_hdr)];
 131
 132                        switch (shutdown_msg->flags) {
 133                        case 0:
 134                        case 1:
 135                                icmsghdrp->status = HV_S_OK;
 136                                execute_shutdown = true;
 137
 138                                pr_info("Shutdown request received -"
 139                                            " graceful shutdown initiated\n");
 140                                break;
 141                        default:
 142                                icmsghdrp->status = HV_E_FAIL;
 143                                execute_shutdown = false;
 144
 145                                pr_info("Shutdown request received -"
 146                                            " Invalid request\n");
 147                                break;
 148                        }
 149                }
 150
 151                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
 152                        | ICMSGHDRFLAG_RESPONSE;
 153
 154                vmbus_sendpacket(channel, shut_txf_buf,
 155                                       recvlen, requestid,
 156                                       VM_PKT_DATA_INBAND, 0);
 157        }
 158
 159        if (execute_shutdown == true)
 160                schedule_work(&shutdown_work);
 161}
 162
 163/*
 164 * Set guest time to host UTC time.
 165 */
 166static inline void do_adj_guesttime(u64 hosttime)
 167{
 168        s64 host_tns;
 169        struct timespec host_ts;
 170
 171        host_tns = (hosttime - WLTIMEDELTA) * 100;
 172        host_ts = ns_to_timespec(host_tns);
 173
 174        do_settimeofday(&host_ts);
 175}
 176
 177/*
 178 * Set the host time in a process context.
 179 */
 180
 181struct adj_time_work {
 182        struct work_struct work;
 183        u64     host_time;
 184};
 185
 186static void hv_set_host_time(struct work_struct *work)
 187{
 188        struct adj_time_work    *wrk;
 189
 190        wrk = container_of(work, struct adj_time_work, work);
 191        do_adj_guesttime(wrk->host_time);
 192        kfree(wrk);
 193}
 194
 195/*
 196 * Synchronize time with host after reboot, restore, etc.
 197 *
 198 * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
 199 * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
 200 * message after the timesync channel is opened. Since the hv_utils module is
 201 * loaded after hv_vmbus, the first message is usually missed. The other
 202 * thing is, systime is automatically set to emulated hardware clock which may
 203 * not be UTC time or in the same time zone. So, to override these effects, we
 204 * use the first 50 time samples for initial system time setting.
 205 */
 206static inline void adj_guesttime(u64 hosttime, u8 flags)
 207{
 208        struct adj_time_work    *wrk;
 209        static s32 scnt = 50;
 210
 211        wrk = kmalloc(sizeof(struct adj_time_work), GFP_ATOMIC);
 212        if (wrk == NULL)
 213                return;
 214
 215        wrk->host_time = hosttime;
 216        if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
 217                INIT_WORK(&wrk->work, hv_set_host_time);
 218                schedule_work(&wrk->work);
 219                return;
 220        }
 221
 222        if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
 223                scnt--;
 224                INIT_WORK(&wrk->work, hv_set_host_time);
 225                schedule_work(&wrk->work);
 226        } else
 227                kfree(wrk);
 228}
 229
 230/*
 231 * Time Sync Channel message handler.
 232 */
 233static void timesync_onchannelcallback(void *context)
 234{
 235        struct vmbus_channel *channel = context;
 236        u32 recvlen;
 237        u64 requestid;
 238        struct icmsg_hdr *icmsghdrp;
 239        struct ictimesync_data *timedatap;
 240        u8 *time_txf_buf = util_timesynch.recv_buffer;
 241        struct icmsg_negotiate *negop = NULL;
 242
 243        vmbus_recvpacket(channel, time_txf_buf,
 244                         PAGE_SIZE, &recvlen, &requestid);
 245
 246        if (recvlen > 0) {
 247                icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
 248                                sizeof(struct vmbuspipe_hdr)];
 249
 250                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
 251                        vmbus_prep_negotiate_resp(icmsghdrp, negop,
 252                                                time_txf_buf,
 253                                                util_fw_version,
 254                                                ts_srv_version);
 255                } else {
 256                        timedatap = (struct ictimesync_data *)&time_txf_buf[
 257                                sizeof(struct vmbuspipe_hdr) +
 258                                sizeof(struct icmsg_hdr)];
 259                        adj_guesttime(timedatap->parenttime, timedatap->flags);
 260                }
 261
 262                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
 263                        | ICMSGHDRFLAG_RESPONSE;
 264
 265                vmbus_sendpacket(channel, time_txf_buf,
 266                                recvlen, requestid,
 267                                VM_PKT_DATA_INBAND, 0);
 268        }
 269}
 270
 271/*
 272 * Heartbeat functionality.
 273 * Every two seconds, Hyper-V send us a heartbeat request message.
 274 * we respond to this message, and Hyper-V knows we are alive.
 275 */
 276static void heartbeat_onchannelcallback(void *context)
 277{
 278        struct vmbus_channel *channel = context;
 279        u32 recvlen;
 280        u64 requestid;
 281        struct icmsg_hdr *icmsghdrp;
 282        struct heartbeat_msg_data *heartbeat_msg;
 283        u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
 284        struct icmsg_negotiate *negop = NULL;
 285
 286        vmbus_recvpacket(channel, hbeat_txf_buf,
 287                         PAGE_SIZE, &recvlen, &requestid);
 288
 289        if (recvlen > 0) {
 290                icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
 291                                sizeof(struct vmbuspipe_hdr)];
 292
 293                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
 294                        vmbus_prep_negotiate_resp(icmsghdrp, negop,
 295                                hbeat_txf_buf, util_fw_version,
 296                                hb_srv_version);
 297                } else {
 298                        heartbeat_msg =
 299                                (struct heartbeat_msg_data *)&hbeat_txf_buf[
 300                                        sizeof(struct vmbuspipe_hdr) +
 301                                        sizeof(struct icmsg_hdr)];
 302
 303                        heartbeat_msg->seq_num += 1;
 304                }
 305
 306                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
 307                        | ICMSGHDRFLAG_RESPONSE;
 308
 309                vmbus_sendpacket(channel, hbeat_txf_buf,
 310                                       recvlen, requestid,
 311                                       VM_PKT_DATA_INBAND, 0);
 312        }
 313}
 314
 315static int util_probe(struct hv_device *dev,
 316                        const struct hv_vmbus_device_id *dev_id)
 317{
 318        struct hv_util_service *srv =
 319                (struct hv_util_service *)dev_id->driver_data;
 320        int ret;
 321
 322        srv->recv_buffer = kmalloc(PAGE_SIZE * 4, GFP_KERNEL);
 323        if (!srv->recv_buffer)
 324                return -ENOMEM;
 325        if (srv->util_init) {
 326                ret = srv->util_init(srv);
 327                if (ret) {
 328                        ret = -ENODEV;
 329                        goto error1;
 330                }
 331        }
 332
 333        /*
 334         * The set of services managed by the util driver are not performance
 335         * critical and do not need batched reading. Furthermore, some services
 336         * such as KVP can only handle one message from the host at a time.
 337         * Turn off batched reading for all util drivers before we open the
 338         * channel.
 339         */
 340
 341        set_channel_read_state(dev->channel, false);
 342
 343        hv_set_drvdata(dev, srv);
 344
 345        /*
 346         * Based on the host; initialize the framework and
 347         * service version numbers we will negotiate.
 348         */
 349        switch (vmbus_proto_version) {
 350        case (VERSION_WS2008):
 351                util_fw_version = UTIL_WS2K8_FW_VERSION;
 352                sd_srv_version = SD_WS2008_VERSION;
 353                ts_srv_version = TS_WS2008_VERSION;
 354                hb_srv_version = HB_WS2008_VERSION;
 355                break;
 356
 357        default:
 358                util_fw_version = UTIL_FW_VERSION;
 359                sd_srv_version = SD_VERSION;
 360                ts_srv_version = TS_VERSION;
 361                hb_srv_version = HB_VERSION;
 362        }
 363
 364        ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
 365                        srv->util_cb, dev->channel);
 366        if (ret)
 367                goto error;
 368
 369        return 0;
 370
 371error:
 372        if (srv->util_deinit)
 373                srv->util_deinit();
 374error1:
 375        kfree(srv->recv_buffer);
 376        return ret;
 377}
 378
 379static int util_remove(struct hv_device *dev)
 380{
 381        struct hv_util_service *srv = hv_get_drvdata(dev);
 382
 383        if (srv->util_deinit)
 384                srv->util_deinit();
 385        vmbus_close(dev->channel);
 386        kfree(srv->recv_buffer);
 387
 388        return 0;
 389}
 390
 391static const struct hv_vmbus_device_id id_table[] = {
 392        /* Shutdown guid */
 393        { HV_SHUTDOWN_GUID,
 394          .driver_data = (unsigned long)&util_shutdown
 395        },
 396        /* Time synch guid */
 397        { HV_TS_GUID,
 398          .driver_data = (unsigned long)&util_timesynch
 399        },
 400        /* Heartbeat guid */
 401        { HV_HEART_BEAT_GUID,
 402          .driver_data = (unsigned long)&util_heartbeat
 403        },
 404        /* KVP guid */
 405        { HV_KVP_GUID,
 406          .driver_data = (unsigned long)&util_kvp
 407        },
 408        /* VSS GUID */
 409        { HV_VSS_GUID,
 410          .driver_data = (unsigned long)&util_vss
 411        },
 412        /* File copy GUID */
 413        { HV_FCOPY_GUID,
 414          .driver_data = (unsigned long)&util_fcopy
 415        },
 416        { },
 417};
 418
 419MODULE_DEVICE_TABLE(vmbus, id_table);
 420
 421/* The one and only one */
 422static  struct hv_driver util_drv = {
 423        .name = "hv_util",
 424        .id_table = id_table,
 425        .probe =  util_probe,
 426        .remove =  util_remove,
 427};
 428
 429static int __init init_hyperv_utils(void)
 430{
 431        pr_info("Registering HyperV Utility Driver\n");
 432
 433        return vmbus_driver_register(&util_drv);
 434}
 435
 436static void exit_hyperv_utils(void)
 437{
 438        pr_info("De-Registered HyperV Utility Driver\n");
 439
 440        vmbus_driver_unregister(&util_drv);
 441}
 442
 443module_init(init_hyperv_utils);
 444module_exit(exit_hyperv_utils);
 445
 446MODULE_DESCRIPTION("Hyper-V Utilities");
 447MODULE_LICENSE("GPL");
 448