linux/drivers/hv/hv_snapshot.c
<<
>>
Prefs
   1/*
   2 * An implementation of host initiated guest snapshot.
   3 *
   4 *
   5 * Copyright (C) 2013, Microsoft, Inc.
   6 * Author : K. Y. Srinivasan <kys@microsoft.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License version 2 as published
  10 * by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  15 * NON INFRINGEMENT.  See the GNU General Public License for more
  16 * details.
  17 *
  18 */
  19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  20
  21#include <linux/net.h>
  22#include <linux/nls.h>
  23#include <linux/connector.h>
  24#include <linux/workqueue.h>
  25#include <linux/hyperv.h>
  26
  27
  28
  29/*
  30 * Global state maintained for transaction that is being processed.
  31 * Note that only one transaction can be active at any point in time.
  32 *
  33 * This state is set when we receive a request from the host; we
  34 * cleanup this state when the transaction is completed - when we respond
  35 * to the host with the key value.
  36 */
  37
  38static struct {
  39        bool active; /* transaction status - active or not */
  40        int recv_len; /* number of bytes received. */
  41        struct vmbus_channel *recv_channel; /* chn we got the request */
  42        u64 recv_req_id; /* request ID. */
  43        struct hv_vss_msg  *msg; /* current message */
  44} vss_transaction;
  45
  46
  47static void vss_respond_to_host(int error);
  48
  49static struct cb_id vss_id = { CN_VSS_IDX, CN_VSS_VAL };
  50static const char vss_name[] = "vss_kernel_module";
  51static __u8 *recv_buffer;
  52
  53static void vss_send_op(struct work_struct *dummy);
  54static DECLARE_WORK(vss_send_op_work, vss_send_op);
  55
  56/*
  57 * Callback when data is received from user mode.
  58 */
  59
  60static void
  61vss_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
  62{
  63        struct hv_vss_msg *vss_msg;
  64
  65        vss_msg = (struct hv_vss_msg *)msg->data;
  66
  67        if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER) {
  68                pr_info("VSS daemon registered\n");
  69                vss_transaction.active = false;
  70                if (vss_transaction.recv_channel != NULL)
  71                        hv_vss_onchannelcallback(vss_transaction.recv_channel);
  72                return;
  73
  74        }
  75        vss_respond_to_host(vss_msg->error);
  76}
  77
  78
  79static void vss_send_op(struct work_struct *dummy)
  80{
  81        int op = vss_transaction.msg->vss_hdr.operation;
  82        struct cn_msg *msg;
  83        struct hv_vss_msg *vss_msg;
  84
  85        msg = kzalloc(sizeof(*msg) + sizeof(*vss_msg), GFP_ATOMIC);
  86        if (!msg)
  87                return;
  88
  89        vss_msg = (struct hv_vss_msg *)msg->data;
  90
  91        msg->id.idx =  CN_VSS_IDX;
  92        msg->id.val = CN_VSS_VAL;
  93
  94        vss_msg->vss_hdr.operation = op;
  95        msg->len = sizeof(struct hv_vss_msg);
  96
  97        cn_netlink_send(msg, 0, GFP_ATOMIC);
  98        kfree(msg);
  99
 100        return;
 101}
 102
 103/*
 104 * Send a response back to the host.
 105 */
 106
 107static void
 108vss_respond_to_host(int error)
 109{
 110        struct icmsg_hdr *icmsghdrp;
 111        u32     buf_len;
 112        struct vmbus_channel *channel;
 113        u64     req_id;
 114
 115        /*
 116         * If a transaction is not active; log and return.
 117         */
 118
 119        if (!vss_transaction.active) {
 120                /*
 121                 * This is a spurious call!
 122                 */
 123                pr_warn("VSS: Transaction not active\n");
 124                return;
 125        }
 126        /*
 127         * Copy the global state for completing the transaction. Note that
 128         * only one transaction can be active at a time.
 129         */
 130
 131        buf_len = vss_transaction.recv_len;
 132        channel = vss_transaction.recv_channel;
 133        req_id = vss_transaction.recv_req_id;
 134        vss_transaction.active = false;
 135
 136        icmsghdrp = (struct icmsg_hdr *)
 137                        &recv_buffer[sizeof(struct vmbuspipe_hdr)];
 138
 139        if (channel->onchannel_callback == NULL)
 140                /*
 141                 * We have raced with util driver being unloaded;
 142                 * silently return.
 143                 */
 144                return;
 145
 146        icmsghdrp->status = error;
 147
 148        icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
 149
 150        vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
 151                                VM_PKT_DATA_INBAND, 0);
 152
 153}
 154
 155/*
 156 * This callback is invoked when we get a VSS message from the host.
 157 * The host ensures that only one VSS transaction can be active at a time.
 158 */
 159
 160void hv_vss_onchannelcallback(void *context)
 161{
 162        struct vmbus_channel *channel = context;
 163        u32 recvlen;
 164        u64 requestid;
 165        struct hv_vss_msg *vss_msg;
 166
 167
 168        struct icmsg_hdr *icmsghdrp;
 169        struct icmsg_negotiate *negop = NULL;
 170
 171        if (vss_transaction.active) {
 172                /*
 173                 * We will defer processing this callback once
 174                 * the current transaction is complete.
 175                 */
 176                vss_transaction.recv_channel = channel;
 177                return;
 178        }
 179
 180        vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
 181                         &requestid);
 182
 183        if (recvlen > 0) {
 184                icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
 185                        sizeof(struct vmbuspipe_hdr)];
 186
 187                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
 188                        vmbus_prep_negotiate_resp(icmsghdrp, negop,
 189                                 recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
 190                        /*
 191                         * We currently negotiate the highest number the
 192                         * host has presented. If this version is not
 193                         * atleast 5.0, reject.
 194                         */
 195                        negop = (struct icmsg_negotiate *)&recv_buffer[
 196                                sizeof(struct vmbuspipe_hdr) +
 197                                sizeof(struct icmsg_hdr)];
 198
 199                        if (negop->icversion_data[1].major < 5)
 200                                negop->icframe_vercnt = 0;
 201                } else {
 202                        vss_msg = (struct hv_vss_msg *)&recv_buffer[
 203                                sizeof(struct vmbuspipe_hdr) +
 204                                sizeof(struct icmsg_hdr)];
 205
 206                        /*
 207                         * Stash away this global state for completing the
 208                         * transaction; note transactions are serialized.
 209                         */
 210
 211                        vss_transaction.recv_len = recvlen;
 212                        vss_transaction.recv_channel = channel;
 213                        vss_transaction.recv_req_id = requestid;
 214                        vss_transaction.active = true;
 215                        vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
 216
 217                        switch (vss_msg->vss_hdr.operation) {
 218                                /*
 219                                 * Initiate a "freeze/thaw"
 220                                 * operation in the guest.
 221                                 * We respond to the host once
 222                                 * the operation is complete.
 223                                 *
 224                                 * We send the message to the
 225                                 * user space daemon and the
 226                                 * operation is performed in
 227                                 * the daemon.
 228                                 */
 229                        case VSS_OP_FREEZE:
 230                        case VSS_OP_THAW:
 231                                schedule_work(&vss_send_op_work);
 232                                return;
 233
 234                        case VSS_OP_HOT_BACKUP:
 235                                vss_msg->vss_cf.flags =
 236                                         VSS_HBU_NO_AUTO_RECOVERY;
 237                                vss_respond_to_host(0);
 238                                return;
 239
 240                        case VSS_OP_GET_DM_INFO:
 241                                vss_msg->dm_info.flags = 0;
 242                                vss_respond_to_host(0);
 243                                return;
 244
 245                        default:
 246                                vss_respond_to_host(0);
 247                                return;
 248
 249                        }
 250
 251                }
 252
 253                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
 254                        | ICMSGHDRFLAG_RESPONSE;
 255
 256                vmbus_sendpacket(channel, recv_buffer,
 257                                       recvlen, requestid,
 258                                       VM_PKT_DATA_INBAND, 0);
 259        }
 260
 261}
 262
 263int
 264hv_vss_init(struct hv_util_service *srv)
 265{
 266        int err;
 267
 268        err = cn_add_callback(&vss_id, vss_name, vss_cn_callback);
 269        if (err)
 270                return err;
 271        recv_buffer = srv->recv_buffer;
 272
 273        /*
 274         * When this driver loads, the user level daemon that
 275         * processes the host requests may not yet be running.
 276         * Defer processing channel callbacks until the daemon
 277         * has registered.
 278         */
 279        vss_transaction.active = true;
 280        return 0;
 281}
 282
 283void hv_vss_deinit(void)
 284{
 285        cn_del_callback(&vss_id);
 286        cancel_work_sync(&vss_send_op_work);
 287}
 288
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.