The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/hyperv/utilities/hv_snapshot.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2016 Microsoft Corp.
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice unmodified, this list of conditions, and the following
   10  *    disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #include <sys/param.h>
   31 #include <sys/kernel.h>
   32 #include <sys/conf.h>
   33 #include <sys/uio.h>
   34 #include <sys/bus.h>
   35 #include <sys/malloc.h>
   36 #include <sys/mbuf.h>
   37 #include <sys/module.h>
   38 #include <sys/lock.h>
   39 #include <sys/taskqueue.h>
   40 #include <sys/selinfo.h>
   41 #include <sys/sysctl.h>
   42 #include <sys/poll.h>
   43 #include <sys/proc.h>
   44 #include <sys/queue.h>
   45 #include <sys/kthread.h>
   46 #include <sys/syscallsubr.h>
   47 #include <sys/sysproto.h>
   48 #include <sys/un.h>
   49 #include <sys/endian.h>
   50 #include <sys/sema.h>
   51 #include <sys/signal.h>
   52 #include <sys/syslog.h>
   53 #include <sys/systm.h>
   54 #include <sys/mutex.h>
   55 #include <sys/callout.h>
   56 
   57 #include <dev/hyperv/include/hyperv.h>
   58 #include <dev/hyperv/utilities/hv_utilreg.h>
   59 #include <dev/hyperv/utilities/vmbus_icreg.h>
   60 #include <dev/hyperv/utilities/vmbus_icvar.h>
   61 
   62 #include "hv_snapshot.h"
   63 #include "vmbus_if.h"
   64 
   65 #define VSS_MAJOR               5
   66 #define VSS_MINOR               0
   67 #define VSS_MSGVER              VMBUS_IC_VERSION(VSS_MAJOR, VSS_MINOR)
   68 
   69 #define VSS_FWVER_MAJOR         3
   70 #define VSS_FWVER               VMBUS_IC_VERSION(VSS_FWVER_MAJOR, 0)
   71 
   72 #define TIMEOUT_LIMIT           (15)    // seconds
   73 enum hv_vss_op {
   74         VSS_OP_CREATE = 0,
   75         VSS_OP_DELETE,
   76         VSS_OP_HOT_BACKUP,
   77         VSS_OP_GET_DM_INFO,
   78         VSS_OP_BU_COMPLETE,
   79         /*
   80          * Following operations are only supported with IC version >= 5.0
   81          */
   82         VSS_OP_FREEZE, /* Freeze the file systems in the VM */
   83         VSS_OP_THAW, /* Unfreeze the file systems */
   84         VSS_OP_AUTO_RECOVER,
   85         VSS_OP_COUNT /* Number of operations, must be last */
   86 };
   87 
   88 /*
   89  * Header for all VSS messages.
   90  */
   91 struct hv_vss_hdr {
   92         struct vmbus_icmsg_hdr  ic_hdr;
   93         uint8_t                 operation;
   94         uint8_t                 reserved[7];
   95 } __packed;
   96 
   97 
   98 /*
   99  * Flag values for the hv_vss_check_feature. Here supports only
  100  * one value.
  101  */
  102 #define VSS_HBU_NO_AUTO_RECOVERY                0x00000005
  103 
  104 struct hv_vss_check_feature {
  105         uint32_t flags;
  106 } __packed;
  107 
  108 struct hv_vss_check_dm_info {
  109         uint32_t flags;
  110 } __packed;
  111 
  112 struct hv_vss_msg {
  113         union {
  114                 struct hv_vss_hdr vss_hdr;
  115         } hdr;
  116         union {
  117                 struct hv_vss_check_feature vss_cf;
  118                 struct hv_vss_check_dm_info dm_info;
  119         } body;
  120 } __packed;
  121 
  122 struct hv_vss_req {
  123         struct hv_vss_opt_msg   opt_msg;        /* used to communicate with daemon */
  124         struct hv_vss_msg       msg;            /* used to communicate with host */
  125 } __packed;
  126 
  127 /* hv_vss debug control */
  128 static int hv_vss_log = 0;
  129 
  130 #define hv_vss_log_error(...)   do {                            \
  131         if (hv_vss_log > 0)                                     \
  132                 log(LOG_ERR, "hv_vss: " __VA_ARGS__);           \
  133 } while (0)
  134 
  135 #define hv_vss_log_info(...) do {                               \
  136         if (hv_vss_log > 1)                                     \
  137                 log(LOG_INFO, "hv_vss: " __VA_ARGS__);          \
  138 } while (0)
  139 
  140 static const struct vmbus_ic_desc vmbus_vss_descs[] = {
  141         {
  142                 .ic_guid = { .hv_guid = {
  143                     0x29, 0x2e, 0xfa, 0x35, 0x23, 0xea, 0x36, 0x42,
  144                     0x96, 0xae, 0x3a, 0x6e, 0xba, 0xcb, 0xa4,  0x40} },
  145                 .ic_desc = "Hyper-V VSS"
  146         },
  147         VMBUS_IC_DESC_END
  148 };
  149 
  150 static const char * vss_opt_name[] = {"None", "VSSCheck", "Freeze", "Thaw"};
  151 
  152 /* character device prototypes */
  153 static d_open_t         hv_vss_dev_open;
  154 static d_close_t        hv_vss_dev_close;
  155 static d_poll_t         hv_vss_dev_daemon_poll;
  156 static d_ioctl_t        hv_vss_dev_daemon_ioctl;
  157 
  158 static d_open_t         hv_appvss_dev_open;
  159 static d_close_t        hv_appvss_dev_close;
  160 static d_poll_t         hv_appvss_dev_poll;
  161 static d_ioctl_t        hv_appvss_dev_ioctl;
  162 
  163 /* hv_vss character device structure */
  164 static struct cdevsw hv_vss_cdevsw =
  165 {
  166         .d_version      = D_VERSION,
  167         .d_open         = hv_vss_dev_open,
  168         .d_close        = hv_vss_dev_close,
  169         .d_poll         = hv_vss_dev_daemon_poll,
  170         .d_ioctl        = hv_vss_dev_daemon_ioctl,
  171         .d_name         = FS_VSS_DEV_NAME,
  172 };
  173 
  174 static struct cdevsw hv_appvss_cdevsw =
  175 {
  176         .d_version      = D_VERSION,
  177         .d_open         = hv_appvss_dev_open,
  178         .d_close        = hv_appvss_dev_close,
  179         .d_poll         = hv_appvss_dev_poll,
  180         .d_ioctl        = hv_appvss_dev_ioctl,
  181         .d_name         = APP_VSS_DEV_NAME,
  182 };
  183 
  184 struct hv_vss_sc;
  185 /*
  186  * Global state to track cdev
  187  */
  188 struct hv_vss_dev_sc {
  189         /*
  190          * msg was transferred from host to notify queue, and
  191          * ack queue. Finally, it was recyled to free list.
  192          */
  193         STAILQ_HEAD(, hv_vss_req_internal)      to_notify_queue;
  194         STAILQ_HEAD(, hv_vss_req_internal)      to_ack_queue;
  195         struct hv_vss_sc                        *sc;
  196         struct proc                             *proc_task;
  197         struct selinfo                          hv_vss_selinfo;
  198 };
  199 /*
  200  * Global state to track and synchronize the transaction requests from the host.
  201  * The VSS allows user to register their function to do freeze/thaw for application.
  202  * VSS kernel will notify both vss daemon and user application if it is registered.
  203  * The implementation state transition is illustrated by:
  204  * https://clovertrail.github.io/assets/vssdot.png
  205  */
  206 typedef struct hv_vss_sc {
  207         struct vmbus_ic_softc                   util_sc;
  208         device_t                                dev;
  209 
  210         struct task                             task;
  211 
  212         /*
  213          * mutex is used to protect access of list/queue,
  214          * callout in request is also used this mutex.
  215          */
  216         struct mtx                              pending_mutex;
  217         /*
  218          * req_free_list contains all free items
  219          */
  220         LIST_HEAD(, hv_vss_req_internal)        req_free_list;
  221 
  222         /* Indicates if daemon registered with driver */
  223         boolean_t                               register_done;
  224 
  225         boolean_t                               app_register_done;
  226 
  227         /* cdev for file system freeze/thaw */
  228         struct cdev                             *hv_vss_dev;
  229         /* cdev for application freeze/thaw */
  230         struct cdev                             *hv_appvss_dev;
  231 
  232         /* sc for app */
  233         struct hv_vss_dev_sc                    app_sc;
  234         /* sc for deamon */
  235         struct hv_vss_dev_sc                    daemon_sc;
  236 } hv_vss_sc;
  237 
  238 typedef struct hv_vss_req_internal {
  239         LIST_ENTRY(hv_vss_req_internal)         link;
  240         STAILQ_ENTRY(hv_vss_req_internal)       slink;
  241         struct hv_vss_req                       vss_req;
  242 
  243         /* Rcv buffer for communicating with the host*/
  244         uint8_t                                 *rcv_buf;
  245         /* Length of host message */
  246         uint32_t                                host_msg_len;
  247         /* Host message id */
  248         uint64_t                                host_msg_id;
  249 
  250         hv_vss_sc                               *sc;
  251 
  252         struct callout                          callout;
  253 } hv_vss_req_internal;
  254 
  255 #define SEARCH_REMOVE_REQ_LOCKED(reqp, queue, link, tmp, id)            \
  256         do {                                                            \
  257                 STAILQ_FOREACH_SAFE(reqp, queue, link, tmp) {           \
  258                         if (reqp->vss_req.opt_msg.msgid == id) {        \
  259                                 STAILQ_REMOVE(queue,                    \
  260                                     reqp, hv_vss_req_internal, link);   \
  261                                 break;                                  \
  262                         }                                               \
  263                 }                                                       \
  264         } while (0)
  265 
  266 static bool
  267 hv_vss_is_daemon_killed_after_launch(hv_vss_sc *sc)
  268 {
  269         return (!sc->register_done && sc->daemon_sc.proc_task);
  270 }
  271 
  272 /*
  273  * Callback routine that gets called whenever there is a message from host
  274  */
  275 static void
  276 hv_vss_callback(struct vmbus_channel *chan __unused, void *context)
  277 {
  278         hv_vss_sc *sc = (hv_vss_sc*)context;
  279         if (hv_vss_is_daemon_killed_after_launch(sc))
  280                 hv_vss_log_info("%s: daemon was killed!\n", __func__);
  281         if (sc->register_done || sc->daemon_sc.proc_task) {
  282                 hv_vss_log_info("%s: Queuing work item\n", __func__);
  283                 if (hv_vss_is_daemon_killed_after_launch(sc))
  284                         hv_vss_log_info("%s: daemon was killed!\n", __func__);
  285                 taskqueue_enqueue(taskqueue_thread, &sc->task);
  286         } else {
  287                 hv_vss_log_info("%s: daemon has never been registered\n", __func__);
  288         }
  289         hv_vss_log_info("%s: received msg from host\n", __func__);
  290 }
  291 /*
  292  * Send the response back to the host.
  293  */
  294 static void
  295 hv_vss_respond_host(uint8_t *rcv_buf, struct vmbus_channel *ch,
  296     uint32_t recvlen, uint64_t requestid, uint32_t error)
  297 {
  298         struct vmbus_icmsg_hdr *hv_icmsg_hdrp;
  299 
  300         hv_icmsg_hdrp = (struct vmbus_icmsg_hdr *)rcv_buf;
  301 
  302         hv_icmsg_hdrp->ic_status = error;
  303         hv_icmsg_hdrp->ic_flags = HV_ICMSGHDRFLAG_TRANSACTION | HV_ICMSGHDRFLAG_RESPONSE;
  304 
  305         error = vmbus_chan_send(ch, VMBUS_CHANPKT_TYPE_INBAND, 0,
  306             rcv_buf, recvlen, requestid);
  307         if (error)
  308                 hv_vss_log_info("%s: hv_vss_respond_host: sendpacket error:%d\n",
  309                     __func__, error);
  310 }
  311 
  312 static void
  313 hv_vss_notify_host_result_locked(struct hv_vss_req_internal *reqp, uint32_t status)
  314 {
  315         struct hv_vss_msg* msg = (struct hv_vss_msg *)reqp->rcv_buf;
  316         hv_vss_sc *sc = reqp->sc;
  317         if (reqp->vss_req.opt_msg.opt == HV_VSS_CHECK) {
  318                 msg->body.vss_cf.flags = VSS_HBU_NO_AUTO_RECOVERY;
  319         }
  320         hv_vss_log_info("%s, %s response %s to host\n", __func__,
  321             vss_opt_name[reqp->vss_req.opt_msg.opt],
  322             status == HV_S_OK ? "Success" : "Fail");
  323         hv_vss_respond_host(reqp->rcv_buf, vmbus_get_channel(reqp->sc->dev),
  324             reqp->host_msg_len, reqp->host_msg_id, status);
  325         /* recycle the request */
  326         LIST_INSERT_HEAD(&sc->req_free_list, reqp, link);
  327 }
  328 
  329 static void
  330 hv_vss_notify_host_result(struct hv_vss_req_internal *reqp, uint32_t status)
  331 {
  332         mtx_lock(&reqp->sc->pending_mutex);
  333         hv_vss_notify_host_result_locked(reqp, status);
  334         mtx_unlock(&reqp->sc->pending_mutex);
  335 }
  336 
  337 static void
  338 hv_vss_cp_vssreq_to_user(struct hv_vss_req_internal *reqp,
  339     struct hv_vss_opt_msg *userdata)
  340 {
  341         struct hv_vss_req *hv_vss_dev_buf;
  342         hv_vss_dev_buf = &reqp->vss_req;
  343         hv_vss_dev_buf->opt_msg.opt = HV_VSS_NONE;
  344         switch (reqp->vss_req.msg.hdr.vss_hdr.operation) {
  345         case VSS_OP_FREEZE:
  346                 hv_vss_dev_buf->opt_msg.opt = HV_VSS_FREEZE;
  347                 break;
  348         case VSS_OP_THAW:
  349                 hv_vss_dev_buf->opt_msg.opt = HV_VSS_THAW;
  350                 break;
  351         case VSS_OP_HOT_BACKUP:
  352                 hv_vss_dev_buf->opt_msg.opt = HV_VSS_CHECK;
  353                 break;
  354         }
  355         *userdata = hv_vss_dev_buf->opt_msg;
  356         hv_vss_log_info("%s, read data from user for "
  357             "%s (%ju) \n", __func__, vss_opt_name[userdata->opt],
  358             (uintmax_t)userdata->msgid);
  359 }
  360 
  361 /**
  362  * Remove the request id from app notifiy or ack queue,
  363  * and recyle the request by inserting it to free list.
  364  *
  365  * When app was notified but not yet sending ack, the request
  366  * should locate in either notify queue or ack queue.
  367  */
  368 static struct hv_vss_req_internal*
  369 hv_vss_drain_req_queue_locked(hv_vss_sc *sc, uint64_t req_id)
  370 {
  371         struct hv_vss_req_internal *reqp, *tmp;
  372         SEARCH_REMOVE_REQ_LOCKED(reqp, &sc->daemon_sc.to_notify_queue,
  373             slink, tmp, req_id);
  374         if (reqp == NULL)
  375                 SEARCH_REMOVE_REQ_LOCKED(reqp, &sc->daemon_sc.to_ack_queue,
  376                     slink, tmp, req_id);
  377         if (reqp == NULL)
  378                 SEARCH_REMOVE_REQ_LOCKED(reqp, &sc->app_sc.to_notify_queue,
  379                     slink, tmp, req_id);
  380         if (reqp == NULL)
  381                 SEARCH_REMOVE_REQ_LOCKED(reqp, &sc->app_sc.to_ack_queue, slink,
  382                     tmp, req_id);
  383         return (reqp);
  384 }
  385 /**
  386  * Actions for daemon who has been notified.
  387  */
  388 static void
  389 hv_vss_notified(struct hv_vss_dev_sc *dev_sc, struct hv_vss_opt_msg *userdata)
  390 {
  391         struct hv_vss_req_internal *reqp;
  392         mtx_lock(&dev_sc->sc->pending_mutex);
  393         if (!STAILQ_EMPTY(&dev_sc->to_notify_queue)) {
  394                 reqp = STAILQ_FIRST(&dev_sc->to_notify_queue);
  395                 hv_vss_cp_vssreq_to_user(reqp, userdata);
  396                 STAILQ_REMOVE_HEAD(&dev_sc->to_notify_queue, slink);
  397                 /* insert the msg to queue for write */
  398                 STAILQ_INSERT_TAIL(&dev_sc->to_ack_queue, reqp, slink);
  399                 userdata->status = VSS_SUCCESS;
  400         } else {
  401                 /* Timeout occur, thus request was removed from queue. */
  402                 hv_vss_log_info("%s: notify queue is empty!\n", __func__);
  403                 userdata->status = VSS_FAIL;
  404         }
  405         mtx_unlock(&dev_sc->sc->pending_mutex);
  406 }
  407 
  408 static void
  409 hv_vss_notify(struct hv_vss_dev_sc *dev_sc, struct hv_vss_req_internal *reqp)
  410 {
  411         uint32_t opt = reqp->vss_req.opt_msg.opt;
  412         mtx_lock(&dev_sc->sc->pending_mutex);
  413         STAILQ_INSERT_TAIL(&dev_sc->to_notify_queue, reqp, slink);
  414         hv_vss_log_info("%s: issuing query %s (%ju) to %s\n", __func__,
  415             vss_opt_name[opt], (uintmax_t)reqp->vss_req.opt_msg.msgid,
  416             &dev_sc->sc->app_sc == dev_sc ? "app" : "daemon");
  417         mtx_unlock(&dev_sc->sc->pending_mutex);
  418         selwakeup(&dev_sc->hv_vss_selinfo);
  419 }
  420 
  421 /**
  422  * Actions for daemon who has acknowledged.
  423  */
  424 static void
  425 hv_vss_daemon_acked(struct hv_vss_dev_sc *dev_sc, struct hv_vss_opt_msg *userdata)
  426 {
  427         struct hv_vss_req_internal      *reqp, *tmp;
  428         uint64_t                        req_id;
  429         int                             opt;
  430         uint32_t                        status;
  431 
  432         opt = userdata->opt;
  433         req_id = userdata->msgid;
  434         status = userdata->status;
  435         /* make sure the reserved fields are all zeros. */
  436         memset(&userdata->reserved, 0, sizeof(struct hv_vss_opt_msg) -
  437             __offsetof(struct hv_vss_opt_msg, reserved));
  438         mtx_lock(&dev_sc->sc->pending_mutex);
  439         SEARCH_REMOVE_REQ_LOCKED(reqp, &dev_sc->to_ack_queue, slink, tmp, req_id);
  440         mtx_unlock(&dev_sc->sc->pending_mutex);
  441         if (reqp == NULL) {
  442                 hv_vss_log_info("%s Timeout: fail to find daemon ack request\n",
  443                     __func__);
  444                 userdata->status = VSS_FAIL;
  445                 return;
  446         }
  447         KASSERT(opt == reqp->vss_req.opt_msg.opt, ("Mismatched VSS operation!"));
  448         hv_vss_log_info("%s, get response %d from daemon for %s (%ju) \n", __func__,
  449             status, vss_opt_name[opt], (uintmax_t)req_id);
  450         switch (opt) {
  451         case HV_VSS_CHECK:
  452         case HV_VSS_FREEZE:
  453                 callout_drain(&reqp->callout);
  454                 hv_vss_notify_host_result(reqp,
  455                     status == VSS_SUCCESS ? HV_S_OK : HV_E_FAIL);
  456                 break;
  457         case HV_VSS_THAW:
  458                 if (dev_sc->sc->app_register_done) {
  459                         if (status == VSS_SUCCESS) {
  460                                 hv_vss_notify(&dev_sc->sc->app_sc, reqp);
  461                         } else {
  462                                 /* handle error */
  463                                 callout_drain(&reqp->callout);
  464                                 hv_vss_notify_host_result(reqp, HV_E_FAIL);
  465                         }
  466                 } else {
  467                         callout_drain(&reqp->callout);
  468                         hv_vss_notify_host_result(reqp,
  469                             status == VSS_SUCCESS ? HV_S_OK : HV_E_FAIL);
  470                 }
  471                 break;
  472         }
  473 }
  474 
  475 /**
  476  * Actions for app who has acknowledged.
  477  */
  478 static void
  479 hv_vss_app_acked(struct hv_vss_dev_sc *dev_sc, struct hv_vss_opt_msg *userdata)
  480 {
  481         struct hv_vss_req_internal      *reqp, *tmp;
  482         uint64_t                        req_id;
  483         int                             opt;
  484         uint8_t                         status;
  485 
  486         opt = userdata->opt;
  487         req_id = userdata->msgid;
  488         status = userdata->status;
  489         /* make sure the reserved fields are all zeros. */
  490         memset(&userdata->reserved, 0, sizeof(struct hv_vss_opt_msg) -
  491             __offsetof(struct hv_vss_opt_msg, reserved));
  492         mtx_lock(&dev_sc->sc->pending_mutex);
  493         SEARCH_REMOVE_REQ_LOCKED(reqp, &dev_sc->to_ack_queue, slink, tmp, req_id);
  494         mtx_unlock(&dev_sc->sc->pending_mutex);
  495         if (reqp == NULL) {
  496                 hv_vss_log_info("%s Timeout: fail to find app ack request\n",
  497                     __func__);
  498                 userdata->status = VSS_FAIL;
  499                 return;
  500         }
  501         KASSERT(opt == reqp->vss_req.opt_msg.opt, ("Mismatched VSS operation!"));
  502         hv_vss_log_info("%s, get response %d from app for %s (%ju) \n",
  503             __func__, status, vss_opt_name[opt], (uintmax_t)req_id);
  504         if (dev_sc->sc->register_done) {
  505                 switch (opt) {
  506                 case HV_VSS_CHECK:
  507                 case HV_VSS_FREEZE:
  508                         if (status == VSS_SUCCESS) {
  509                                 hv_vss_notify(&dev_sc->sc->daemon_sc, reqp);
  510                         } else {
  511                                 /* handle error */
  512                                 callout_drain(&reqp->callout);
  513                                 hv_vss_notify_host_result(reqp, HV_E_FAIL);
  514                         }
  515                         break;
  516                 case HV_VSS_THAW:
  517                         callout_drain(&reqp->callout);
  518                         hv_vss_notify_host_result(reqp,
  519                             status == VSS_SUCCESS ? HV_S_OK : HV_E_FAIL);
  520                         break;
  521                 }
  522         } else {
  523                 hv_vss_log_info("%s, Fatal: vss daemon was killed\n", __func__);
  524         }
  525 }
  526 
  527 static int
  528 hv_vss_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
  529 {
  530         struct proc     *td_proc;
  531         td_proc = td->td_proc;
  532 
  533         struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
  534         hv_vss_log_info("%s: %s opens device \"%s\" successfully.\n",
  535             __func__, td_proc->p_comm, FS_VSS_DEV_NAME);
  536 
  537         if (dev_sc->sc->register_done)
  538                 return (EBUSY);
  539 
  540         dev_sc->sc->register_done = true;
  541         hv_vss_callback(vmbus_get_channel(dev_sc->sc->dev), dev_sc->sc);
  542 
  543         dev_sc->proc_task = curproc;
  544         return (0);
  545 }
  546 
  547 static int
  548 hv_vss_dev_close(struct cdev *dev, int fflag __unused, int devtype __unused,
  549                                  struct thread *td)
  550 {
  551         struct proc     *td_proc;
  552         td_proc = td->td_proc;
  553 
  554         struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
  555 
  556         hv_vss_log_info("%s: %s closes device \"%s\"\n",
  557             __func__, td_proc->p_comm, FS_VSS_DEV_NAME);
  558         dev_sc->sc->register_done = false;
  559         return (0);
  560 }
  561 
  562 static int
  563 hv_vss_dev_daemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
  564     struct thread *td)
  565 {
  566         struct proc                     *td_proc;
  567         struct hv_vss_dev_sc            *sc;
  568 
  569         td_proc = td->td_proc;
  570         sc = (struct hv_vss_dev_sc*)dev->si_drv1;
  571 
  572         hv_vss_log_info("%s: %s invoked vss ioctl\n", __func__, td_proc->p_comm);
  573 
  574         struct hv_vss_opt_msg* userdata = (struct hv_vss_opt_msg*)data;
  575         switch(cmd) {
  576         case IOCHVVSSREAD:
  577                 hv_vss_notified(sc, userdata);
  578                 break;
  579         case IOCHVVSSWRITE:
  580                 hv_vss_daemon_acked(sc, userdata);
  581                 break;
  582         }
  583         return (0);
  584 }
  585 
  586 /*
  587  * hv_vss_daemon poll invokes this function to check if data is available
  588  * for daemon to read.
  589  */
  590 static int
  591 hv_vss_dev_daemon_poll(struct cdev *dev, int events, struct thread *td)
  592 {
  593         int revent = 0;
  594         struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
  595 
  596         mtx_lock(&dev_sc->sc->pending_mutex);
  597         /**
  598          * if there is data ready, inform daemon's poll
  599          */
  600         if (!STAILQ_EMPTY(&dev_sc->to_notify_queue))
  601                 revent = POLLIN;
  602         if (revent == 0)
  603                 selrecord(td, &dev_sc->hv_vss_selinfo);
  604         hv_vss_log_info("%s return 0x%x\n", __func__, revent);
  605         mtx_unlock(&dev_sc->sc->pending_mutex);
  606         return (revent);
  607 }
  608 
  609 static int
  610 hv_appvss_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
  611 {
  612         struct proc     *td_proc;
  613         td_proc = td->td_proc;
  614 
  615         struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
  616         hv_vss_log_info("%s: %s opens device \"%s\" successfully.\n",
  617             __func__, td_proc->p_comm, APP_VSS_DEV_NAME);
  618 
  619         if (dev_sc->sc->app_register_done)
  620                 return (EBUSY);
  621 
  622         dev_sc->sc->app_register_done = true;
  623         dev_sc->proc_task = curproc;
  624         return (0);
  625 }
  626 
  627 static int
  628 hv_appvss_dev_close(struct cdev *dev, int fflag __unused, int devtype __unused,
  629                                  struct thread *td)
  630 {
  631         struct proc     *td_proc;
  632         td_proc = td->td_proc;
  633 
  634         struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
  635 
  636         hv_vss_log_info("%s: %s closes device \"%s\".\n",
  637             __func__, td_proc->p_comm, APP_VSS_DEV_NAME);
  638         dev_sc->sc->app_register_done = false;
  639         return (0);
  640 }
  641 
  642 static int
  643 hv_appvss_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
  644     struct thread *td)
  645 {
  646         struct proc                     *td_proc;
  647         struct hv_vss_dev_sc            *dev_sc;
  648 
  649         td_proc = td->td_proc;
  650         dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
  651 
  652         hv_vss_log_info("%s: %s invoked vss ioctl\n", __func__, td_proc->p_comm);
  653 
  654         struct hv_vss_opt_msg* userdata = (struct hv_vss_opt_msg*)data;
  655         switch(cmd) {
  656         case IOCHVVSSREAD:
  657                 hv_vss_notified(dev_sc, userdata);
  658                 break;
  659         case IOCHVVSSWRITE:
  660                 hv_vss_app_acked(dev_sc, userdata);
  661                 break;
  662         }
  663         return (0);
  664 }
  665 
  666 /*
  667  * hv_vss_daemon poll invokes this function to check if data is available
  668  * for daemon to read.
  669  */
  670 static int
  671 hv_appvss_dev_poll(struct cdev *dev, int events, struct thread *td)
  672 {
  673         int revent = 0;
  674         struct hv_vss_dev_sc *dev_sc = (struct hv_vss_dev_sc*)dev->si_drv1;
  675 
  676         mtx_lock(&dev_sc->sc->pending_mutex);
  677         /**
  678          * if there is data ready, inform daemon's poll
  679          */
  680         if (!STAILQ_EMPTY(&dev_sc->to_notify_queue))
  681                 revent = POLLIN;
  682         if (revent == 0)
  683                 selrecord(td, &dev_sc->hv_vss_selinfo);
  684         hv_vss_log_info("%s return 0x%x\n", __func__, revent);
  685         mtx_unlock(&dev_sc->sc->pending_mutex);
  686         return (revent);
  687 }
  688 
  689 static void
  690 hv_vss_timeout(void *arg)
  691 {
  692         hv_vss_req_internal *reqp = arg;
  693         hv_vss_req_internal *request __diagused;
  694         hv_vss_sc* sc = reqp->sc;
  695         uint64_t req_id = reqp->vss_req.opt_msg.msgid;
  696         /* This thread is locked */
  697         KASSERT(mtx_owned(&sc->pending_mutex), ("mutex lock is not owned!"));
  698         request = hv_vss_drain_req_queue_locked(sc, req_id);
  699         KASSERT(request != NULL, ("timeout but fail to find request"));
  700         hv_vss_notify_host_result_locked(reqp, HV_E_FAIL);
  701 }
  702 
  703 /*
  704  * This routine is called whenever a message is received from the host
  705  */
  706 static void
  707 hv_vss_init_req(hv_vss_req_internal *reqp,
  708     uint32_t recvlen, uint64_t requestid, uint8_t *vss_buf, hv_vss_sc *sc)
  709 {
  710         struct timespec vm_ts;
  711         struct hv_vss_msg* msg = (struct hv_vss_msg *)vss_buf;
  712 
  713         memset(reqp, 0, __offsetof(hv_vss_req_internal, callout));
  714         reqp->host_msg_len = recvlen;
  715         reqp->host_msg_id = requestid;
  716         reqp->rcv_buf = vss_buf;
  717         reqp->sc = sc;
  718         memcpy(&reqp->vss_req.msg,
  719             (struct hv_vss_msg *)vss_buf, sizeof(struct hv_vss_msg));
  720         /* set the opt for users */
  721         switch (msg->hdr.vss_hdr.operation) {
  722         case VSS_OP_FREEZE:
  723                 reqp->vss_req.opt_msg.opt = HV_VSS_FREEZE;
  724                 break;
  725         case VSS_OP_THAW:
  726                 reqp->vss_req.opt_msg.opt = HV_VSS_THAW;
  727                 break;
  728         case VSS_OP_HOT_BACKUP:
  729                 reqp->vss_req.opt_msg.opt = HV_VSS_CHECK;
  730                 break;
  731         }
  732         /* Use a timestamp as msg request ID */
  733         nanotime(&vm_ts);
  734         reqp->vss_req.opt_msg.msgid = (vm_ts.tv_sec * NANOSEC) + vm_ts.tv_nsec;
  735 }
  736 
  737 static hv_vss_req_internal*
  738 hv_vss_get_new_req_locked(hv_vss_sc *sc)
  739 {
  740         hv_vss_req_internal *reqp;
  741         if (!STAILQ_EMPTY(&sc->daemon_sc.to_notify_queue) ||
  742             !STAILQ_EMPTY(&sc->daemon_sc.to_ack_queue) ||
  743             !STAILQ_EMPTY(&sc->app_sc.to_notify_queue) ||
  744             !STAILQ_EMPTY(&sc->app_sc.to_ack_queue)) {
  745                 /*
  746                  * There is request coming from host before
  747                  * finishing previous requests
  748                  */
  749                 hv_vss_log_info("%s: Warning: there is new request "
  750                     "coming before finishing previous requests\n", __func__);
  751                 return (NULL);
  752         }
  753         if (LIST_EMPTY(&sc->req_free_list)) {
  754                 /* TODO Error: no buffer */
  755                 hv_vss_log_info("Error: No buffer\n");
  756                 return (NULL);
  757         }
  758         reqp = LIST_FIRST(&sc->req_free_list);
  759         LIST_REMOVE(reqp, link);
  760         return (reqp);
  761 }
  762 
  763 static void
  764 hv_vss_start_notify(hv_vss_req_internal *reqp, uint32_t opt)
  765 {
  766         hv_vss_sc *sc = reqp->sc;
  767         /*
  768          * Freeze/Check notification sequence: kernel -> app -> daemon(fs)
  769          * Thaw notification sequence:         kernel -> daemon(fs) -> app
  770          *
  771          * We should wake up the daemon, in case it's doing poll().
  772          * The response should be received after 5s, otherwise, trigger timeout.
  773          */
  774         switch (opt) {
  775         case VSS_OP_FREEZE:
  776         case VSS_OP_HOT_BACKUP:
  777                 if (sc->app_register_done)
  778                         hv_vss_notify(&sc->app_sc, reqp);
  779                 else
  780                         hv_vss_notify(&sc->daemon_sc, reqp);
  781                 callout_reset(&reqp->callout, TIMEOUT_LIMIT * hz,
  782                     hv_vss_timeout, reqp);
  783                 break;
  784         case VSS_OP_THAW:
  785                 hv_vss_notify(&sc->daemon_sc, reqp);
  786                 callout_reset(&reqp->callout, TIMEOUT_LIMIT * hz,
  787                     hv_vss_timeout, reqp);
  788                 break;
  789         }
  790 }
  791 
  792 /*
  793  * Function to read the vss request buffer from host
  794  * and interact with daemon
  795  */
  796 static void
  797 hv_vss_process_request(void *context, int pending __unused)
  798 {
  799         uint8_t *vss_buf;
  800         struct vmbus_channel *channel;
  801         uint32_t recvlen = 0;
  802         uint64_t requestid;
  803         struct vmbus_icmsg_hdr *icmsghdrp;
  804         int ret = 0;
  805         hv_vss_sc *sc;
  806         hv_vss_req_internal *reqp;
  807 
  808         hv_vss_log_info("%s: entering hv_vss_process_request\n", __func__);
  809 
  810         sc = (hv_vss_sc*)context;
  811         vss_buf = sc->util_sc.ic_buf;
  812         channel = vmbus_get_channel(sc->dev);
  813 
  814         recvlen = sc->util_sc.ic_buflen;
  815         ret = vmbus_chan_recv(channel, vss_buf, &recvlen, &requestid);
  816         KASSERT(ret != ENOBUFS, ("hvvss recvbuf is not large enough"));
  817         /* XXX check recvlen to make sure that it contains enough data */
  818 
  819         while ((ret == 0) && (recvlen > 0)) {
  820                 icmsghdrp = (struct vmbus_icmsg_hdr *)vss_buf;
  821 
  822                 if (icmsghdrp->ic_type == HV_ICMSGTYPE_NEGOTIATE) {
  823                         ret = vmbus_ic_negomsg(&sc->util_sc, vss_buf,
  824                             &recvlen, VSS_FWVER, VSS_MSGVER);
  825                         hv_vss_respond_host(vss_buf, vmbus_get_channel(sc->dev),
  826                             recvlen, requestid, ret);
  827                         hv_vss_log_info("%s: version negotiated\n", __func__);
  828                 } else if (!hv_vss_is_daemon_killed_after_launch(sc)) {
  829                         struct hv_vss_msg* msg = (struct hv_vss_msg *)vss_buf;
  830                         switch(msg->hdr.vss_hdr.operation) {
  831                         case VSS_OP_FREEZE:
  832                         case VSS_OP_THAW:
  833                         case VSS_OP_HOT_BACKUP:
  834                                 mtx_lock(&sc->pending_mutex);
  835                                 reqp = hv_vss_get_new_req_locked(sc);
  836                                 mtx_unlock(&sc->pending_mutex);
  837                                 if (reqp == NULL) {
  838                                         /* ignore this request from host */
  839                                         break;
  840                                 }
  841                                 hv_vss_init_req(reqp, recvlen, requestid, vss_buf, sc);
  842                                 hv_vss_log_info("%s: receive %s (%ju) from host\n",
  843                                     __func__,
  844                                     vss_opt_name[reqp->vss_req.opt_msg.opt],
  845                                     (uintmax_t)reqp->vss_req.opt_msg.msgid);
  846                                 hv_vss_start_notify(reqp, msg->hdr.vss_hdr.operation);
  847                                 break;
  848                         case VSS_OP_GET_DM_INFO:
  849                                 hv_vss_log_info("%s: receive GET_DM_INFO from host\n",
  850                                     __func__);
  851                                 msg->body.dm_info.flags = 0;
  852                                 hv_vss_respond_host(vss_buf, vmbus_get_channel(sc->dev),
  853                                     recvlen, requestid, HV_S_OK);
  854                                 break;
  855                         default:
  856                                 device_printf(sc->dev, "Unknown opt from host: %d\n",
  857                                     msg->hdr.vss_hdr.operation);
  858                                 break;
  859                         }
  860                 } else {
  861                         /* daemon was killed for some reason after it was launched */
  862                         struct hv_vss_msg* msg = (struct hv_vss_msg *)vss_buf;
  863                         switch(msg->hdr.vss_hdr.operation) {
  864                         case VSS_OP_FREEZE:
  865                                 hv_vss_log_info("%s: response fail for FREEZE\n",
  866                                     __func__);
  867                                 break;
  868                         case VSS_OP_THAW:
  869                                 hv_vss_log_info("%s: response fail for THAW\n",
  870                                     __func__);
  871                                 break;
  872                         case VSS_OP_HOT_BACKUP:
  873                                 hv_vss_log_info("%s: response fail for HOT_BACKUP\n",
  874                                     __func__);
  875                                 msg->body.vss_cf.flags = VSS_HBU_NO_AUTO_RECOVERY;
  876                                 break;
  877                         case VSS_OP_GET_DM_INFO:
  878                                 hv_vss_log_info("%s: response fail for GET_DM_INFO\n",
  879                                     __func__);
  880                                 msg->body.dm_info.flags = 0;
  881                                 break;
  882                         default:
  883                                 device_printf(sc->dev, "Unknown opt from host: %d\n",
  884                                     msg->hdr.vss_hdr.operation);
  885                                 break;
  886                         }
  887                         hv_vss_respond_host(vss_buf, vmbus_get_channel(sc->dev),
  888                             recvlen, requestid, HV_E_FAIL);
  889                 }
  890                 /*
  891                  * Try reading next buffer
  892                  */
  893                 recvlen = sc->util_sc.ic_buflen;
  894                 ret = vmbus_chan_recv(channel, vss_buf, &recvlen, &requestid);
  895                 KASSERT(ret != ENOBUFS, ("hvvss recvbuf is not large enough"));
  896                 /* XXX check recvlen to make sure that it contains enough data */
  897 
  898                 hv_vss_log_info("%s: read: context %p, ret =%d, recvlen=%d\n",
  899                     __func__, context, ret, recvlen);
  900         }
  901 }
  902 
  903 static int
  904 hv_vss_probe(device_t dev)
  905 {
  906         return (vmbus_ic_probe(dev, vmbus_vss_descs));
  907 }
  908 
  909 static int
  910 hv_vss_init_send_receive_queue(device_t dev)
  911 {
  912         hv_vss_sc *sc = (hv_vss_sc*)device_get_softc(dev);
  913         int i;
  914         const int max_list = 4; /* It is big enough for the list */
  915         struct hv_vss_req_internal* reqp;
  916 
  917         LIST_INIT(&sc->req_free_list);
  918         STAILQ_INIT(&sc->daemon_sc.to_notify_queue);
  919         STAILQ_INIT(&sc->daemon_sc.to_ack_queue);
  920         STAILQ_INIT(&sc->app_sc.to_notify_queue);
  921         STAILQ_INIT(&sc->app_sc.to_ack_queue);
  922 
  923         for (i = 0; i < max_list; i++) {
  924                 reqp = malloc(sizeof(struct hv_vss_req_internal),
  925                     M_DEVBUF, M_WAITOK|M_ZERO);
  926                 LIST_INSERT_HEAD(&sc->req_free_list, reqp, link);
  927                 callout_init_mtx(&reqp->callout, &sc->pending_mutex, 0);
  928         }
  929         return (0);
  930 }
  931 
  932 static int
  933 hv_vss_destroy_send_receive_queue(device_t dev)
  934 {
  935         hv_vss_sc *sc = (hv_vss_sc*)device_get_softc(dev);
  936         hv_vss_req_internal* reqp;
  937 
  938         while (!LIST_EMPTY(&sc->req_free_list)) {
  939                 reqp = LIST_FIRST(&sc->req_free_list);
  940                 LIST_REMOVE(reqp, link);
  941                 free(reqp, M_DEVBUF);
  942         }
  943 
  944         while (!STAILQ_EMPTY(&sc->daemon_sc.to_notify_queue)) {
  945                 reqp = STAILQ_FIRST(&sc->daemon_sc.to_notify_queue);
  946                 STAILQ_REMOVE_HEAD(&sc->daemon_sc.to_notify_queue, slink);
  947                 free(reqp, M_DEVBUF);
  948         }
  949 
  950         while (!STAILQ_EMPTY(&sc->daemon_sc.to_ack_queue)) {
  951                 reqp = STAILQ_FIRST(&sc->daemon_sc.to_ack_queue);
  952                 STAILQ_REMOVE_HEAD(&sc->daemon_sc.to_ack_queue, slink);
  953                 free(reqp, M_DEVBUF);
  954         }
  955 
  956         while (!STAILQ_EMPTY(&sc->app_sc.to_notify_queue)) {
  957                 reqp = STAILQ_FIRST(&sc->app_sc.to_notify_queue);
  958                 STAILQ_REMOVE_HEAD(&sc->app_sc.to_notify_queue, slink);
  959                 free(reqp, M_DEVBUF);
  960         }
  961 
  962         while (!STAILQ_EMPTY(&sc->app_sc.to_ack_queue)) {
  963                 reqp = STAILQ_FIRST(&sc->app_sc.to_ack_queue);
  964                 STAILQ_REMOVE_HEAD(&sc->app_sc.to_ack_queue, slink);
  965                 free(reqp, M_DEVBUF);
  966         }
  967         return (0);
  968 }
  969 
  970 static int
  971 hv_vss_attach(device_t dev)
  972 {
  973         int error;
  974         struct sysctl_oid_list *child;
  975         struct sysctl_ctx_list *ctx;
  976 
  977         hv_vss_sc *sc = (hv_vss_sc*)device_get_softc(dev);
  978 
  979         sc->dev = dev;
  980         mtx_init(&sc->pending_mutex, "hv_vss pending mutex", NULL, MTX_DEF);
  981 
  982         ctx = device_get_sysctl_ctx(dev);
  983         child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
  984 
  985         SYSCTL_ADD_INT(ctx, child, OID_AUTO, "hv_vss_log",
  986             CTLFLAG_RWTUN, &hv_vss_log, 0, "Hyperv VSS service log level");
  987 
  988         TASK_INIT(&sc->task, 0, hv_vss_process_request, sc);
  989         hv_vss_init_send_receive_queue(dev);
  990         /* create character device for file system freeze/thaw */
  991         error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK,
  992                     &sc->hv_vss_dev,
  993                     &hv_vss_cdevsw,
  994                     0,
  995                     UID_ROOT,
  996                     GID_WHEEL,
  997                     0640,
  998                     FS_VSS_DEV_NAME);
  999 
 1000         if (error != 0) {
 1001                 hv_vss_log_info("Fail to create '%s': %d\n", FS_VSS_DEV_NAME, error);
 1002                 return (error);
 1003         }
 1004         sc->hv_vss_dev->si_drv1 = &sc->daemon_sc;
 1005         sc->daemon_sc.sc = sc;
 1006         /* create character device for application freeze/thaw */
 1007         error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK,
 1008                     &sc->hv_appvss_dev,
 1009                     &hv_appvss_cdevsw,
 1010                     0,
 1011                     UID_ROOT,
 1012                     GID_WHEEL,
 1013                     0640,
 1014                     APP_VSS_DEV_NAME);
 1015 
 1016         if (error != 0) {
 1017                 hv_vss_log_info("Fail to create '%s': %d\n", APP_VSS_DEV_NAME, error);
 1018                 return (error);
 1019         }
 1020         sc->hv_appvss_dev->si_drv1 = &sc->app_sc;
 1021         sc->app_sc.sc = sc;
 1022 
 1023         return (vmbus_ic_attach(dev, hv_vss_callback));
 1024 }
 1025 
 1026 static int
 1027 hv_vss_detach(device_t dev)
 1028 {
 1029         hv_vss_sc *sc = (hv_vss_sc*)device_get_softc(dev);
 1030         mtx_destroy(&sc->pending_mutex);
 1031         if (sc->daemon_sc.proc_task != NULL) {
 1032                 PROC_LOCK(sc->daemon_sc.proc_task);
 1033                 kern_psignal(sc->daemon_sc.proc_task, SIGKILL);
 1034                 PROC_UNLOCK(sc->daemon_sc.proc_task);
 1035         }
 1036         if (sc->app_sc.proc_task != NULL) {
 1037                 PROC_LOCK(sc->app_sc.proc_task);
 1038                 kern_psignal(sc->app_sc.proc_task, SIGKILL);
 1039                 PROC_UNLOCK(sc->app_sc.proc_task);
 1040         }
 1041         hv_vss_destroy_send_receive_queue(dev);
 1042         destroy_dev(sc->hv_vss_dev);
 1043         destroy_dev(sc->hv_appvss_dev);
 1044         return (vmbus_ic_detach(dev));
 1045 }
 1046 
 1047 static device_method_t vss_methods[] = {
 1048         /* Device interface */
 1049         DEVMETHOD(device_probe, hv_vss_probe),
 1050         DEVMETHOD(device_attach, hv_vss_attach),
 1051         DEVMETHOD(device_detach, hv_vss_detach),
 1052         { 0, 0 }
 1053 };
 1054 
 1055 static driver_t vss_driver = { "hvvss", vss_methods, sizeof(hv_vss_sc)};
 1056 
 1057 DRIVER_MODULE(hv_vss, vmbus, vss_driver, NULL, NULL);
 1058 MODULE_VERSION(hv_vss, 1);
 1059 MODULE_DEPEND(hv_vss, vmbus, 1, 1, 1);

Cache object: d23282171bb62c7ed8cc2cc260a278e5


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.