if (vmbus_recvpacket(channel, shut_txf_buf, HV_HYP_PAGE_SIZE, &recvlen, &requestid)) {
pr_err_ratelimited("Shutdown request received. Could not read into shut txf buf\n"); return;
}
if (!recvlen) return;
/* Ensure recvlen is big enough to read header data */ if (recvlen < ICMSG_HDR) {
pr_err_ratelimited("Shutdown request received. Packet length too small: %d\n",
recvlen); return;
}
/* * shutdown_msg->flags can be 0(shut down), 2(reboot), * or 4(hibernate). It may bitwise-OR 1, which means * performing the request by force. Linux always tries * to perform the request by force.
*/ switch (shutdown_msg->flags) { case 0: case 1:
icmsghdrp->status = HV_S_OK;
work = &shutdown_work;
pr_info("Shutdown request received - graceful shutdown initiated\n"); break; case 2: case 3:
icmsghdrp->status = HV_S_OK;
work = &restart_work;
pr_info("Restart request received - graceful restart initiated\n"); break; case 4: case 5:
pr_info("Hibernation request received\n");
icmsghdrp->status = hibernation_supported ?
HV_S_OK : HV_E_FAIL; if (hibernation_supported)
work = &hibernate_context.work; break; default:
icmsghdrp->status = HV_E_FAIL;
pr_info("Shutdown request received - Invalid request\n"); break;
}
} else {
icmsghdrp->status = HV_E_FAIL;
pr_err_ratelimited("Shutdown request received. Invalid msg type: %d\n",
icmsghdrp->icmsgtype);
}
/* * Set the host time in a process context.
*/ staticstruct work_struct adj_time_work;
/* * The last time sample, received from the host. PTP device responds to * requests by using this data and the current partition-wide time reference * count.
*/ staticstruct {
u64 host_time;
u64 ref_time;
spinlock_t lock;
} host_ts;
staticbool timesync_implicit;
module_param(timesync_implicit, bool, 0644);
MODULE_PARM_DESC(timesync_implicit, "If set treat SAMPLE as SYNC when clock is behind");
/* * We need to let the caller know that last update from host * is older than the max allowable threshold. clock_gettime() * and PTP ioctl do not have a documented error that we could * return for this specific case. Use ESTALE to report this.
*/
timediff_adj = reftime - host_ts.ref_time; if (timediff_adj * 100 > HOST_TIMESYNC_DELAY_THRESH) {
pr_warn_once("TIMESYNC IC: Stale time stamp, %llu nsecs old\n",
(timediff_adj * 100));
ret = -ESTALE;
}
if (!hv_get_adj_host_time(&ts))
do_settimeofday64(&ts);
}
/* * Due to a bug on Hyper-V hosts, the sync flag may not always be sent on resume. * Force a sync if the guest is behind.
*/ staticinlinebool hv_implicit_sync(u64 host_time)
{ struct timespec64 new_ts; struct timespec64 threshold_ts;
/* * If guest behind the host by 5 or more seconds.
*/ if (timespec64_compare(&new_ts, &threshold_ts) >= 0) returntrue;
returnfalse;
}
/* * Synchronize time with host after reboot, restore, etc. * * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time * message after the timesync channel is opened. Since the hv_utils module is * loaded after hv_vmbus, the first message is usually missed. This bit is * considered a hard request to discipline the clock. * * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is * typically used as a hint to the guest. The guest is under no obligation * to discipline the clock.
*/ staticinlinevoid adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags)
{ unsignedlong flags;
u64 cur_reftime;
/* * Save the adjusted time sample from the host and the snapshot * of the current system time.
*/
spin_lock_irqsave(&host_ts.lock, flags);
/* * TimeSync v4 messages contain reference time (guest's Hyper-V * clocksource read when the time sample was generated), we can * improve the precision by adding the delta between now and the * time of generation. For older protocols we set * reftime == cur_reftime on call.
*/
host_ts.host_time += (cur_reftime - reftime);
spin_unlock_irqrestore(&host_ts.lock, flags);
/* Schedule work to do do_settimeofday64() */ if ((adj_flags & ICTIMESYNCFLAG_SYNC) ||
(timesync_implicit && hv_implicit_sync(host_ts.host_time)))
schedule_work(&adj_time_work);
}
/* * Drain the ring buffer and use the last packet to update * host_ts
*/ while (1) { int ret = vmbus_recvpacket(channel, time_txf_buf,
HV_HYP_PAGE_SIZE, &recvlen,
&requestid); if (ret) {
pr_err_ratelimited("TimeSync IC pkt recv failed (Err: %d)\n",
ret); break;
}
if (!recvlen) break;
/* Ensure recvlen is big enough to read header data */ if (recvlen < ICMSG_HDR) {
pr_err_ratelimited("Timesync request received. Packet length too small: %d\n",
recvlen); break;
}
/* * Heartbeat functionality. * Every two seconds, Hyper-V send us a heartbeat request message. * we respond to this message, and Hyper-V knows we are alive.
*/ staticvoid heartbeat_onchannelcallback(void *context)
{ struct vmbus_channel *channel = context;
u32 recvlen;
u64 requestid; struct icmsg_hdr *icmsghdrp; struct heartbeat_msg_data *heartbeat_msg;
u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
while (1) {
if (vmbus_recvpacket(channel, hbeat_txf_buf, HV_HYP_PAGE_SIZE,
&recvlen, &requestid)) {
pr_err_ratelimited("Heartbeat request received. Could not read into hbeat txf buf\n"); return;
}
if (!recvlen) break;
/* Ensure recvlen is big enough to read header data */ if (recvlen < ICMSG_HDR) {
pr_err_ratelimited("Heartbeat request received. Packet length too small: %d\n",
recvlen); break;
}
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (vmbus_prep_negotiate_resp(icmsghdrp,
hbeat_txf_buf, recvlen,
fw_versions, FW_VER_COUNT,
hb_versions, HB_VER_COUNT,
NULL, &hb_srv_version)) {
pr_info("Heartbeat IC version %d.%d\n",
hb_srv_version >> 16,
hb_srv_version & 0xFFFF);
}
} elseif (icmsghdrp->icmsgtype == ICMSGTYPE_HEARTBEAT) { /* * Ensure recvlen is big enough to read seq_num. Reserved area is not * included in the check as the host may not fill it up entirely
*/ if (recvlen < ICMSG_HDR + sizeof(u64)) {
pr_err_ratelimited("Invalid heartbeat msg data. Length too small: %u\n",
recvlen); break;
}
heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ICMSG_HDR];
srv->recv_buffer = kmalloc(HV_HYP_PAGE_SIZE * 4, GFP_KERNEL); if (!srv->recv_buffer) return -ENOMEM;
srv->channel = dev->channel; if (srv->util_init) {
ret = srv->util_init(srv); if (ret) goto error1;
}
/* * The set of services managed by the util driver are not performance * critical and do not need batched reading. Furthermore, some services * such as KVP can only handle one message from the host at a time. * Turn off batched reading for all util drivers before we open the * channel.
*/
set_channel_read_mode(dev->channel, HV_CALL_DIRECT);
hv_set_drvdata(dev, srv);
ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE,
HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb,
dev->channel); if (ret) goto error;
if (srv->util_init_transport) {
ret = srv->util_init_transport(); if (ret) {
vmbus_close(dev->channel); goto error;
}
} return 0;
error: if (srv->util_deinit)
srv->util_deinit();
error1:
kfree(srv->recv_buffer); return ret;
}
if (srv->util_deinit)
srv->util_deinit();
vmbus_close(dev->channel);
kfree(srv->recv_buffer);
}
/* * When we're in util_suspend(), all the userspace processes have been frozen * (refer to hibernate() -> freeze_processes()). The userspace is thawed only * after the whole resume procedure, including util_resume(), finishes.
*/ staticint util_suspend(struct hv_device *dev)
{ struct hv_util_service *srv = hv_get_drvdata(dev); int ret = 0;
if (srv->util_pre_suspend) {
ret = srv->util_pre_suspend(); if (ret) return ret;
}
vmbus_close(dev->channel);
return 0;
}
staticint util_resume(struct hv_device *dev)
{ struct hv_util_service *srv = hv_get_drvdata(dev); int ret = 0;
if (srv->util_pre_resume) {
ret = srv->util_pre_resume(); if (ret) return ret;
}
/* * ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is * disabled but the driver is still useful without the PTP device * as it still handles the ICTIMESYNCFLAG_SYNC case.
*/
hv_ptp_clock = ptp_clock_register(&ptp_hyperv_info, NULL); if (IS_ERR_OR_NULL(hv_ptp_clock)) {
pr_err("cannot register PTP clock: %d\n",
PTR_ERR_OR_ZERO(hv_ptp_clock));
hv_ptp_clock = NULL;
}
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.