/* See comment with struct hv_vss_msg regarding the max VMbus packet size */ #define VSS_MAX_PKT_SIZE (HV_HYP_PAGE_SIZE * 2)
/* * Timeout values are based on expecations from host
*/ #define VSS_FREEZE_TIMEOUT (15 * 60)
/* * 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. * * 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.
*/
staticstruct { 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. */ struct hv_vss_msg *msg; /* current message */
} vss_transaction;
staticvoid vss_respond_to_host(int error);
/* * This state maintains the version number registered by the daemon.
*/ staticint dm_reg_value;
staticvoid vss_poll_wrapper(void *channel)
{ /* Transaction is finished, reset the state here to avoid races. */
vss_transaction.state = HVUTIL_READY;
tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event);
}
/* * Callback when data is received from user mode.
*/
staticvoid vss_timeout_func(struct work_struct *dummy)
{ /* * 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);
if (len != sizeof(*vss_msg)) {
pr_debug("VSS: Message size does not match length\n"); 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) {
pr_debug("VSS: Got unexpected registration request\n"); return -EINVAL;
}
if (vss_msg->vss_hdr.operation == VSS_OP_HOT_BACKUP)
vss_transaction.msg->vss_cf.flags =
VSS_HBU_NO_AUTO_RECOVERY;
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_debug("VSS: Transaction not active\n"); return -EINVAL;
} return 0;
}
staticvoid vss_send_op(void)
{ int op = vss_transaction.msg->vss_hdr.operation; int rc; struct hv_vss_msg *vss_msg;
/* The transaction state is wrong. */ if (vss_transaction.state != HVUTIL_HOSTMSG_RECEIVED) {
pr_debug("VSS: Unexpected attempt to send to daemon\n"); return;
}
vss_msg = kzalloc(sizeof(*vss_msg), GFP_KERNEL); if (!vss_msg) return;
vss_msg->vss_hdr.operation = op;
vss_transaction.state = HVUTIL_USERSPACE_REQ;
schedule_delayed_work(&vss_timeout_work, op == VSS_OP_FREEZE ?
secs_to_jiffies(VSS_FREEZE_TIMEOUT) :
secs_to_jiffies(HV_UTIL_TIMEOUT));
rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL); 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);
}
staticvoid vss_handle_request(struct work_struct *dummy)
{ switch (vss_transaction.msg->vss_hdr.operation) { /* * Initiate a "freeze/thaw" operation in the guest. * We respond to the host once the operation is complete. * * We send the message to the user space daemon and the operation is * performed in the daemon.
*/ case VSS_OP_THAW: case VSS_OP_FREEZE: case VSS_OP_HOT_BACKUP: if (vss_transaction.state < HVUTIL_READY) { /* Userspace is not registered yet */
pr_debug("VSS: Not ready for request.\n");
vss_respond_to_host(HV_E_FAIL); return;
}
pr_debug("VSS: Received request for op code: %d\n",
vss_transaction.msg->vss_hdr.operation);
vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
vss_send_op(); return; case VSS_OP_GET_DM_INFO:
vss_transaction.msg->dm_info.flags = 0; break; default: break;
}
if (vmbus_recvpacket(channel, recv_buffer, VSS_MAX_PKT_SIZE, &recvlen, &requestid)) {
pr_err_ratelimited("VSS request received. Could not read into recv buf\n"); return;
}
if (!recvlen) return;
/* Ensure recvlen is big enough to read header data */ if (recvlen < ICMSG_HDR) {
pr_err_ratelimited("VSS request received. Packet length too small: %d\n",
recvlen); return;
}
staticvoid 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;
}
int
hv_vss_init(struct hv_util_service *srv)
{ 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;
vss_transaction.recv_channel = srv->channel;
vss_transaction.recv_channel->max_pkt_size = VSS_MAX_PKT_SIZE;
/* * When this driver loads, the user level daemon that * processes the host requests may not yet be running. * Defer processing channel callbacks until the daemon * has registered.
*/
vss_transaction.state = HVUTIL_DEVICE_INIT;
return 0;
}
int
hv_vss_init_transport(void)
{
hvt = hvutil_transport_init(vss_devname, CN_VSS_IDX, CN_VSS_VAL,
vss_on_msg, vss_on_reset); if (!hvt) {
pr_warn("VSS: Failed to initialize transport\n"); return -EFAULT;
}
/* * Fake a THAW message for the user space daemon in case the daemon * has frozen the file systems. It doesn't matter if there is already * a message pending to be delivered to the user space since we force * vss_transaction.state to be HVUTIL_READY, so the user space daemon's * write() will fail with EINVAL (see vss_on_msg()), and the daemon * will reset the device by closing and re-opening it.
*/
vss_msg = kzalloc(sizeof(*vss_msg), GFP_KERNEL); if (!vss_msg) return -ENOMEM;
tasklet_disable(&channel->callback_event);
vss_msg->vss_hdr.operation = VSS_OP_THAW;
/* Cancel any possible pending work. */
hv_vss_cancel_work();
/* We don't care about the return value. */
hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL);
kfree(vss_msg);
vss_transaction.state = HVUTIL_READY;
/* tasklet_enable() will be called in hv_vss_pre_resume(). */ return 0;
}
int hv_vss_pre_resume(void)
{ struct vmbus_channel *channel = vss_transaction.recv_channel;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.