static void timesync_onchannelcallback()

in hv_util.c [394:480]


static void timesync_onchannelcallback(void *context)
{
	struct vmbus_channel *channel = context;
	u32 recvlen;
	u64 requestid;
	struct icmsg_hdr *icmsghdrp;
	struct ictimesync_data *timedatap;
	struct ictimesync_ref_data *refdata;
	u8 *time_txf_buf = util_timesynch.recv_buffer;

	/*
	 * 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;
		}

		icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
				sizeof(struct vmbuspipe_hdr)];

		if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
			if (vmbus_prep_negotiate_resp(icmsghdrp,
						time_txf_buf, recvlen,
						fw_versions, FW_VER_COUNT,
						ts_versions, TS_VER_COUNT,
						NULL, &ts_srv_version)) {
				pr_info("TimeSync IC version %d.%d\n",
					ts_srv_version >> 16,
					ts_srv_version & 0xFFFF);
			}
		} else if (icmsghdrp->icmsgtype == ICMSGTYPE_TIMESYNC) {
			if (ts_srv_version > TS_VERSION_3) {
				/* Ensure recvlen is big enough to read ictimesync_ref_data */
				if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_ref_data)) {
					pr_err_ratelimited("Invalid ictimesync ref data. Length too small: %u\n",
							   recvlen);
					break;
				}
				refdata = (struct ictimesync_ref_data *)&time_txf_buf[ICMSG_HDR];

				adj_guesttime(refdata->parenttime,
						refdata->vmreferencetime,
						refdata->flags);
			} else {
				/* Ensure recvlen is big enough to read ictimesync_data */
				if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_data)) {
					pr_err_ratelimited("Invalid ictimesync data. Length too small: %u\n",
							   recvlen);
					break;
				}
				timedatap = (struct ictimesync_data *)&time_txf_buf[ICMSG_HDR];

				adj_guesttime(timedatap->parenttime,
					      hv_read_reference_counter(),
					      timedatap->flags);
			}
		} else {
			icmsghdrp->status = HV_E_FAIL;
			pr_err_ratelimited("Timesync request received. Invalid msg type: %d\n",
					   icmsghdrp->icmsgtype);
		}

		icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
			| ICMSGHDRFLAG_RESPONSE;

		vmbus_sendpacket(channel, time_txf_buf,
				 recvlen, requestid,
				 VM_PKT_DATA_INBAND, 0);
	}
}