--- zzzz-none-000/linux-3.10.107/drivers/hv/hv_snapshot.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/hv/hv_snapshot.c 2021-02-04 17:41:59.000000000 +0000 @@ -24,19 +24,31 @@ #include #include +#include "hyperv_vmbus.h" +#include "hv_utils_transport.h" +#define VSS_MAJOR 5 +#define VSS_MINOR 0 +#define VSS_VERSION (VSS_MAJOR << 16 | VSS_MINOR) + +#define VSS_USERSPACE_TIMEOUT (msecs_to_jiffies(10 * 1000)) /* - * Global state maintained for transaction that is being processed. - * Note that only one transaction can be active at any point in time. + * Global state maintained for transaction that is being processed. For a class + * of integration services, including the "VSS service", the specified protocol + * is a "request/response" protocol which means that there can only be single + * outstanding transaction from the host at any given point in time. We use + * this to simplify memory management in this driver - we cache and process + * only one message at a time. * - * This state is set when we receive a request from the host; we - * cleanup this state when the transaction is completed - when we respond - * to the host with the key value. + * While the request/response protocol is guaranteed by the host, we further + * ensure this by serializing packet processing in this driver - we do not + * read additional packets from the VMBUs until the current packet is fully + * handled. */ static struct { - bool active; /* transaction status - active or not */ + int state; /* hvutil_device_state */ int recv_len; /* number of bytes received. */ struct vmbus_channel *recv_channel; /* chn we got the request */ u64 recv_req_id; /* request ID. */ @@ -46,56 +58,127 @@ static void vss_respond_to_host(int error); -static struct cb_id vss_id = { CN_VSS_IDX, CN_VSS_VAL }; -static const char vss_name[] = "vss_kernel_module"; +/* + * This state maintains the version number registered by the daemon. + */ +static int dm_reg_value; + +static const char vss_devname[] = "vmbus/hv_vss"; static __u8 *recv_buffer; +static struct hvutil_transport *hvt; +static struct completion release_event; static void vss_send_op(struct work_struct *dummy); +static void vss_timeout_func(struct work_struct *dummy); + +static DECLARE_DELAYED_WORK(vss_timeout_work, vss_timeout_func); static DECLARE_WORK(vss_send_op_work, vss_send_op); +static void vss_poll_wrapper(void *channel) +{ + /* Transaction is finished, reset the state here to avoid races. */ + vss_transaction.state = HVUTIL_READY; + hv_vss_onchannelcallback(channel); +} + /* * Callback when data is received from user mode. */ -static void -vss_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +static void vss_timeout_func(struct work_struct *dummy) { - struct hv_vss_msg *vss_msg; + /* + * Timeout waiting for userspace component to reply happened. + */ + pr_warn("VSS: timeout waiting for daemon to reply\n"); + vss_respond_to_host(HV_E_FAIL); - vss_msg = (struct hv_vss_msg *)msg->data; + hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper); +} - if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER) { - pr_info("VSS daemon registered\n"); - vss_transaction.active = false; - if (vss_transaction.recv_channel != NULL) - hv_vss_onchannelcallback(vss_transaction.recv_channel); - return; +static int vss_handle_handshake(struct hv_vss_msg *vss_msg) +{ + u32 our_ver = VSS_OP_REGISTER1; + + switch (vss_msg->vss_hdr.operation) { + case VSS_OP_REGISTER: + /* Daemon doesn't expect us to reply */ + dm_reg_value = VSS_OP_REGISTER; + break; + case VSS_OP_REGISTER1: + /* Daemon expects us to reply with our own version*/ + if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver))) + return -EFAULT; + dm_reg_value = VSS_OP_REGISTER1; + break; + default: + return -EINVAL; + } + vss_transaction.state = HVUTIL_READY; + pr_debug("VSS: userspace daemon ver. %d registered\n", dm_reg_value); + return 0; +} + +static int vss_on_msg(void *msg, int len) +{ + struct hv_vss_msg *vss_msg = (struct hv_vss_msg *)msg; + if (len != sizeof(*vss_msg)) + return -EINVAL; + + if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER || + vss_msg->vss_hdr.operation == VSS_OP_REGISTER1) { + /* + * Don't process registration messages if we're in the middle + * of a transaction processing. + */ + if (vss_transaction.state > HVUTIL_READY) + return -EINVAL; + return vss_handle_handshake(vss_msg); + } else if (vss_transaction.state == HVUTIL_USERSPACE_REQ) { + vss_transaction.state = HVUTIL_USERSPACE_RECV; + if (cancel_delayed_work_sync(&vss_timeout_work)) { + vss_respond_to_host(vss_msg->error); + /* Transaction is finished, reset the state. */ + hv_poll_channel(vss_transaction.recv_channel, + vss_poll_wrapper); + } + } else { + /* This is a spurious call! */ + pr_warn("VSS: Transaction not active\n"); + return -EINVAL; } - vss_respond_to_host(vss_msg->error); + return 0; } static void vss_send_op(struct work_struct *dummy) { int op = vss_transaction.msg->vss_hdr.operation; - struct cn_msg *msg; + int rc; struct hv_vss_msg *vss_msg; - msg = kzalloc(sizeof(*msg) + sizeof(*vss_msg), GFP_ATOMIC); - if (!msg) + /* The transaction state is wrong. */ + if (vss_transaction.state != HVUTIL_HOSTMSG_RECEIVED) return; - vss_msg = (struct hv_vss_msg *)msg->data; - - msg->id.idx = CN_VSS_IDX; - msg->id.val = CN_VSS_VAL; + vss_msg = kzalloc(sizeof(*vss_msg), GFP_KERNEL); + if (!vss_msg) + return; vss_msg->vss_hdr.operation = op; - msg->len = sizeof(struct hv_vss_msg); - cn_netlink_send(msg, 0, GFP_ATOMIC); - kfree(msg); + vss_transaction.state = HVUTIL_USERSPACE_REQ; + rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg)); + if (rc) { + pr_warn("VSS: failed to communicate to the daemon: %d\n", rc); + if (cancel_delayed_work_sync(&vss_timeout_work)) { + vss_respond_to_host(HV_E_FAIL); + vss_transaction.state = HVUTIL_READY; + } + } + + kfree(vss_msg); return; } @@ -113,17 +196,6 @@ u64 req_id; /* - * If a transaction is not active; log and return. - */ - - if (!vss_transaction.active) { - /* - * This is a spurious call! - */ - pr_warn("VSS: Transaction not active\n"); - return; - } - /* * Copy the global state for completing the transaction. Note that * only one transaction can be active at a time. */ @@ -131,7 +203,6 @@ buf_len = vss_transaction.recv_len; channel = vss_transaction.recv_channel; req_id = vss_transaction.recv_req_id; - vss_transaction.active = false; icmsghdrp = (struct icmsg_hdr *) &recv_buffer[sizeof(struct vmbuspipe_hdr)]; @@ -168,14 +239,8 @@ struct icmsg_hdr *icmsghdrp; struct icmsg_negotiate *negop = NULL; - if (vss_transaction.active) { - /* - * We will defer processing this callback once - * the current transaction is complete. - */ - vss_transaction.recv_channel = channel; + if (vss_transaction.state > HVUTIL_READY) return; - } vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, &requestid); @@ -186,18 +251,8 @@ if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { vmbus_prep_negotiate_resp(icmsghdrp, negop, - recv_buffer, MAX_SRV_VER, MAX_SRV_VER); - /* - * We currently negotiate the highest number the - * host has presented. If this version is not - * atleast 5.0, reject. - */ - negop = (struct icmsg_negotiate *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - if (negop->icversion_data[1].major < 5) - negop->icframe_vercnt = 0; + recv_buffer, UTIL_FW_VERSION, + VSS_VERSION); } else { vss_msg = (struct hv_vss_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + @@ -211,7 +266,6 @@ vss_transaction.recv_len = recvlen; vss_transaction.recv_channel = channel; vss_transaction.recv_req_id = requestid; - vss_transaction.active = true; vss_transaction.msg = (struct hv_vss_msg *)vss_msg; switch (vss_msg->vss_hdr.operation) { @@ -228,7 +282,15 @@ */ case VSS_OP_FREEZE: case VSS_OP_THAW: + if (vss_transaction.state < HVUTIL_READY) { + /* Userspace is not registered yet */ + vss_respond_to_host(HV_E_FAIL); + return; + } + vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED; schedule_work(&vss_send_op_work); + schedule_delayed_work(&vss_timeout_work, + VSS_USERSPACE_TIMEOUT); return; case VSS_OP_HOT_BACKUP: @@ -260,14 +322,23 @@ } +static void vss_on_reset(void) +{ + if (cancel_delayed_work_sync(&vss_timeout_work)) + vss_respond_to_host(HV_E_FAIL); + vss_transaction.state = HVUTIL_DEVICE_INIT; + complete(&release_event); +} + int hv_vss_init(struct hv_util_service *srv) { - int err; - - err = cn_add_callback(&vss_id, vss_name, vss_cn_callback); - if (err) - return err; + init_completion(&release_event); + if (vmbus_proto_version < VERSION_WIN8_1) { + pr_warn("Integration service 'Backup (volume snapshot)'" + " not supported on this host version.\n"); + return -ENOTSUPP; + } recv_buffer = srv->recv_buffer; /* @@ -276,12 +347,21 @@ * Defer processing channel callbacks until the daemon * has registered. */ - vss_transaction.active = true; + vss_transaction.state = HVUTIL_DEVICE_INIT; + + hvt = hvutil_transport_init(vss_devname, CN_VSS_IDX, CN_VSS_VAL, + vss_on_msg, vss_on_reset); + if (!hvt) + return -EFAULT; + return 0; } void hv_vss_deinit(void) { - cn_del_callback(&vss_id); + vss_transaction.state = HVUTIL_DEVICE_DYING; + cancel_delayed_work_sync(&vss_timeout_work); cancel_work_sync(&vss_send_op_work); + hvutil_transport_destroy(hvt); + wait_for_completion(&release_event); }