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