Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package hyper-v for openSUSE:Factory checked in at 2024-10-23 21:07:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/hyper-v (Old) and /work/SRC/openSUSE:Factory/.hyper-v.new.26871 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "hyper-v" Wed Oct 23 21:07:50 2024 rev:46 rq:1217113 version:9 Changes: -------- --- /work/SRC/openSUSE:Factory/hyper-v/hyper-v.changes 2024-05-01 14:55:45.064381936 +0200 +++ /work/SRC/openSUSE:Factory/.hyper-v.new.26871/hyper-v.changes 2024-10-23 21:07:51.556665304 +0200 @@ -1,0 +2,22 @@ +Tue Oct 22 22:22:22 UTC 2024 - oher...@suse.de + +- Add memory allocation check in hv_fcopy_start (94e86b17) +- suppress the invalid warning for packed member alignment (207e03b0) +- Add new fcopy application based on uio driver (82b0945c) +- Add vmbus_bufring (45bab4d7) +- kvp: Handle IPv4 and Ipv6 combination for keyfile format (f971f6dd) +- kvp: Some small fixes for handling NM keyfiles (c3803203) +- kvp: Support for keyfile based connection profile (42999c90) +- kvp: remove unnecessary (void*) conversions (22589542) +- Remove an extraneous "the" (f15f39fa) +- change http to https in hv_kvp_daemon.c (fa52a4b2) +- replace the copy of include/linux/hyperv.h with include/uapi/linux/hyperv.h (6de74d10) +- merge individual udev rules files into a single rules file +- package only files, not directories already owned by filesystem.rpm +- remove braces from rpm spec macros +- remove obsolete Group tag +- replace RPM_BUILD_ROOT with buildroot +- use a meaningful name for the UAPI include file +- use a meaningful variable name for ifcfg in hv_set_ifconfig.sh + +------------------------------------------------------------------- New: ---- hyper-v.tools.hv.hv_fcopy_uio_daemon.c hyper-v.tools.hv.vmbus_bufring.c hyper-v.tools.hv.vmbus_bufring.h ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ hyper-v.spec ++++++ --- /var/tmp/diff_new_pack.NsqYXP/_old 2024-10-23 21:07:52.452702504 +0200 +++ /var/tmp/diff_new_pack.NsqYXP/_new 2024-10-23 21:07:52.456702669 +0200 @@ -19,7 +19,13 @@ %define hv_kvp_daemon hv_kvp_daemon %define hv_vss_daemon hv_vss_daemon %define hv_fcopy_daemon hv_fcopy_daemon -%define helper_dir /usr/lib/%{name} +%define vmbus_bufring vmbus_bufring +%define hv_fcopy_uio_daemon hv_fcopy_uio_daemon +%define include_uapi_linux_hyperv include_uapi_linux_hyperv +%define chardev_kvp vmbus/hv_kvp +%define chardev_vss vmbus/hv_vss +%define chardev_fcopy vmbus/hv_fcopy +%define helper_dir /usr/lib/%name Name: hyper-v ExclusiveArch: %ix86 x86_64 aarch64 @@ -30,19 +36,21 @@ Conflicts: kernel < 4.2 Summary: Microsoft Hyper-V tools License: GPL-2.0-only -Group: System/Kernel Supplements: modalias(dmi:*svnMicrosoftCorporation*pnVirtualMachine*rnVirtualMachine*) Supplements: modalias(pci:v00001414d00005353sv*sd*bc*sc*i*) URL: http://www.kernel.org # Arbitrary version number -Version: 8 +Version: 9 Release: 0 Source0: hyper-v.lsvmbus.py Source5: hyper-v.kvptest.ps1.txt Source7: hyper-v.compare-with-upstream.sh +Source8: hyper-v.tools.hv.vmbus_bufring.h Source9: hyper-v.include.linux.hyperv.h Source10: hyper-v.tools.hv.hv_kvp_daemon.c +Source11: hyper-v.tools.hv.vmbus_bufring.c Source12: hyper-v.tools.hv.hv_vss_daemon.c +Source13: hyper-v.tools.hv.hv_fcopy_uio_daemon.c Source14: hyper-v.tools.hv.hv_fcopy_daemon.c Source20: hyper-v.tools.hv.hv_get_dhcp_info.sh Source21: hyper-v.tools.hv.hv_get_dns_info.sh @@ -56,107 +64,147 @@ %prep %setup -Tc cp -avL %{S:5} kvptest.ps1.txt -cp -vL %{S:9} %{hv_kvp_daemon}.h +cp -vL %{S:8} %vmbus_bufring.h +cp -vL %{S:9} %include_uapi_linux_hyperv.h cp -vL %{S:10} . -cp -vL %{S:12} %{hv_vss_daemon}.c -cp -vL %{S:14} %{hv_fcopy_daemon}.c +cp -vL %{S:11} %vmbus_bufring.c +cp -vL %{S:12} %hv_vss_daemon.c +cp -vL %{S:13} %hv_fcopy_uio_daemon.c +cp -vL %{S:14} %hv_fcopy_daemon.c %patch -P 0 -p1 -mv `basename %{S:10}` %{hv_kvp_daemon}.c +mv `basename %{S:10}` %hv_kvp_daemon.c %build -sed -i~ '/#include <linux.hyperv.h>/d' %{hv_kvp_daemon}.c -sed -i~ '/#include <linux.hyperv.h>/d' %{hv_vss_daemon}.c -sed -i~ '/#include <linux.hyperv.h>/d' %{hv_fcopy_daemon}.c +sed -i~ '/#include <linux.hyperv.h>/d' %hv_kvp_daemon.c +sed -i~ '/#include <linux.hyperv.h>/d' %hv_vss_daemon.c +sed -i~ '/#include <linux.hyperv.h>/d' %hv_fcopy_uio_daemon.c +sed -i~ '/#include <linux.hyperv.h>/d' %hv_fcopy_daemon.c gcc \ $RPM_OPT_FLAGS \ -Wno-unused-variable \ -Wno-pointer-sign \ -D_GNU_SOURCE \ -g \ - %{hv_kvp_daemon}.c \ - -include %{hv_kvp_daemon}.h \ + %hv_kvp_daemon.c \ + -include %include_uapi_linux_hyperv.h \ -DCN_KVP_IDX=0x9 \ -DCN_KVP_VAL=0x1 \ -DKVP_SCRIPTS_PATH= \ -pthread \ - -o %{hv_kvp_daemon} + -o %hv_kvp_daemon gcc \ $RPM_OPT_FLAGS \ -Wno-unused-variable \ -Wno-pointer-sign \ -D_GNU_SOURCE \ -g \ - %{hv_vss_daemon}.c \ - -include %{hv_kvp_daemon}.h \ + %hv_vss_daemon.c \ + -include %include_uapi_linux_hyperv.h \ -DCN_VSS_IDX=0xa \ -DCN_VSS_VAL=0x1 \ - -o %{hv_vss_daemon} + -o %hv_vss_daemon gcc \ $RPM_OPT_FLAGS \ -Wno-unused-variable \ -Wno-pointer-sign \ -D_GNU_SOURCE \ -g \ - %{hv_fcopy_daemon}.c \ - -include %{hv_kvp_daemon}.h \ - -o %{hv_fcopy_daemon} + %hv_fcopy_daemon.c \ + -include %include_uapi_linux_hyperv.h \ + -o %hv_fcopy_daemon + +%ifarch %ix86 x86_64 +gcc \ + $RPM_OPT_FLAGS \ + -Wno-unused-variable \ + -Wno-pointer-sign \ + -Wno-address-of-packed-member \ + -D_GNU_SOURCE \ + -g \ + %vmbus_bufring.c \ + %hv_fcopy_uio_daemon.c \ + -include %include_uapi_linux_hyperv.h \ + -o %hv_fcopy_uio_daemon +%endif %install # It is not a callable app anyway, so move it out of the way -bindir=%{helper_dir}/bin -mkdir -p $RPM_BUILD_ROOT${bindir} -mkdir -p $RPM_BUILD_ROOT%{_sbindir} -mkdir -p $RPM_BUILD_ROOT%{helper_dir}/bin -install -m755 %{hv_kvp_daemon} $RPM_BUILD_ROOT${bindir} -install -m755 %{hv_vss_daemon} $RPM_BUILD_ROOT${bindir} -install -m755 %{hv_fcopy_daemon} $RPM_BUILD_ROOT${bindir} -cp -avL %{S:0} $RPM_BUILD_ROOT%{_sbindir}/lsvmbus -chmod 0755 $RPM_BUILD_ROOT%{_sbindir}/lsvmbus -cp -avL %{S:20} $RPM_BUILD_ROOT%{helper_dir}/bin/hv_get_dhcp_info -cp -avL %{S:21} $RPM_BUILD_ROOT%{helper_dir}/bin/hv_get_dns_info -cp -avL %{S:22} $RPM_BUILD_ROOT%{helper_dir}/bin/hv_set_ifconfig -chmod 755 $RPM_BUILD_ROOT%{helper_dir}/bin/* -d=$RPM_BUILD_ROOT%{_unitdir} +bindir=%helper_dir/bin +mkdir -p %buildroot${bindir} +mkdir -p %buildroot%_sbindir +install -m755 %hv_kvp_daemon %buildroot${bindir} +install -m755 %hv_vss_daemon %buildroot${bindir} +install -m755 %hv_fcopy_daemon %buildroot${bindir} +%ifarch %ix86 x86_64 +install -m755 %hv_fcopy_uio_daemon %buildroot${bindir} +%endif +cp -avL %{S:0} %buildroot%_sbindir/lsvmbus +chmod 0755 %buildroot%_sbindir/lsvmbus +cp -avL %{S:20} %buildroot${bindir}/hv_get_dhcp_info +cp -avL %{S:21} %buildroot${bindir}/hv_get_dns_info +cp -avL %{S:22} %buildroot${bindir}/hv_set_ifconfig +chmod 755 %buildroot${bindir}/* +d=%buildroot%_unitdir mkdir -vp ${d} # -tee ${d}/%{hv_kvp_daemon}.service <<EOF -# started via %{_udevrulesdir}/%{hv_kvp_daemon}.rules +tee ${d}/%hv_kvp_daemon.service <<EOF +# started via %_udevrulesdir/%name.rules [Unit] Description=Hyper-V KVP Daemon After=local-fs.target ConditionVirtualization=microsoft +ConditionPathExists=/dev/%chardev_kvp [Service] -Environment="PATH=%{helper_dir}/bin:/usr/sbin:/usr/bin:/sbin:/bin" -ExecStart=${bindir}/%{hv_kvp_daemon} --no-daemon +Environment="PATH=${bindir}:/usr/sbin:/usr/bin:/sbin:/bin" +ExecStart=${bindir}/%hv_kvp_daemon --no-daemon Restart=on-failure [Install] WantedBy=default.target EOF # -tee ${d}/%{hv_vss_daemon}.service <<EOF -# started via %{_udevrulesdir}/%{hv_vss_daemon}.rules +tee ${d}/%hv_vss_daemon.service <<EOF +# started via %_udevrulesdir/%name.rules [Unit] Description=Hyper-V VSS Daemon ConditionVirtualization=microsoft +ConditionPathExists=/dev/%chardev_vss [Service] -ExecStart=${bindir}/%{hv_vss_daemon} --no-daemon +ExecStart=${bindir}/%hv_vss_daemon --no-daemon Restart=on-failure [Install] WantedBy=default.target EOF # -tee ${d}/%{hv_fcopy_daemon}.service <<EOF -# started via %{_udevrulesdir}/%{hv_fcopy_daemon}.rules +tee ${d}/%hv_fcopy_daemon.service <<EOF +# started via %_udevrulesdir/%name.rules [Unit] Description=Hyper-V host to guest file copy daemon ConditionVirtualization=microsoft +ConditionPathExists=/dev/%chardev_fcopy + +[Service] +ExecStart=${bindir}/%hv_fcopy_daemon --no-daemon +Restart=on-failure + +[Install] +WantedBy=default.target +EOF +# +tee ${d}/%hv_fcopy_uio_daemon.service <<EOF +# started via %_udevrulesdir/%name.rules +[Unit] +Description=Hyper-V host to guest UIO file copy daemon +ConditionVirtualization=microsoft +ConditionPathExists=/sys/bus/vmbus/drivers/uio_hv_generic/new_id +ConditionPathExists=!/dev/%chardev_fcopy [Service] -ExecStart=${bindir}/%{hv_fcopy_daemon} --no-daemon +ExecStartPre=/bin/sh -c 'set -e;echo 34d14be3-dee4-41c8-9ae7-6b174977c192 > /sys/bus/vmbus/drivers/uio_hv_generic/new_id' +ExecStart=${bindir}/%hv_fcopy_uio_daemon --no-daemon Restart=on-failure [Install] @@ -165,53 +213,61 @@ # # # -d=$RPM_BUILD_ROOT%{_udevrulesdir} +d=%buildroot%_udevrulesdir mkdir -vp ${d} -tee ${d}/%{hv_kvp_daemon}.rules <<EOF -ACTION=="add", KERNEL=="vmbus/hv_kvp", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%{hv_kvp_daemon}.service" -EOF -tee ${d}/%{hv_vss_daemon}.rules <<EOF -ACTION=="add", KERNEL=="vmbus/hv_vss", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%{hv_vss_daemon}.service" -EOF -tee ${d}/%{hv_fcopy_daemon}.rules <<EOF -ACTION=="add", KERNEL=="vmbus/hv_fcopy", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%{hv_fcopy_daemon}.service" +tee ${d}/%name.rules <<EOF +ACTION=="add", KERNEL=="%chardev_kvp", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%hv_kvp_daemon.service" +ACTION=="add", KERNEL=="%chardev_vss", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%hv_vss_daemon.service" +ACTION=="add", KERNEL=="%chardev_fcopy", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%hv_fcopy_daemon.service" +ACTION=="add", SUBSYSTEM=="vmbus", ATTRS{class_id}=="{34d14be3-dee4-41c8-9ae7-6b174977c192}", RUN{builtin}+="kmod load uio_hv_generic", TAG+="systemd", ENV{SYSTEMD_WANTS}+="%hv_fcopy_uio_daemon.service" EOF # # helper=inst_sys.sh -tee $RPM_BUILD_ROOT${bindir}/${helper} <<EOF +tee %buildroot${bindir}/${helper} <<'_EOF_' #!/bin/bash # Starting daemons via RUN== in udev rules is not supported. # In inst-sys systemd is not used, so start all daemons manually. +bindir=%helper_dir/bin +declare -a helpers +test -c /dev/%chardev_kvp && helpers+=(%hv_kvp_daemon) +test -c /dev/%chardev_vss && helpers+=(%hv_vss_daemon) +if test -c /dev/%chardev_fcopy +then + helpers+=(%hv_fcopy_daemon) +else + modprobe -v uio_hv_generic + if test -f /sys/bus/vmbus/drivers/uio_hv_generic/new_id + then + echo '34d14be3-dee4-41c8-9ae7-6b174977c192' > /sys/bus/vmbus/drivers/uio_hv_generic/new_id + helpers+=(%hv_fcopy_uio_daemon) + fi +fi if test -d /sys/bus/vmbus/devices then - export PATH=${bindir}:\$PATH + export PATH=${bindir}:$PATH echo -n "Starting hyper-v helpers:" - for i in \ - %{hv_kvp_daemon} \ - %{hv_vss_daemon} \ - %{hv_fcopy_daemon} + for i in "${helpers[@]}" do - if mkdir /run/\$i + if mkdir /run/$i then - echo -n " \$i" - \$i < /dev/null & + echo -n " $i" + $i < /dev/null & fi done echo " ... done" fi -EOF -chmod 755 $RPM_BUILD_ROOT${bindir}/${helper} +_EOF_ +chmod 755 %buildroot${bindir}/${helper} # -%python3_fix_shebang +%?python3_fix_shebang %files %doc kvptest.ps1.txt -%{_unitdir} -%dir /usr/lib/udev -%{_udevrulesdir} -%{_sbindir}/* -%{helper_dir} +%_unitdir/* +%_udevrulesdir/* +%_sbindir/* +%helper_dir %pre # hv_kvp_daemon in SLES11 SP2 stored temporary state files in /var/opt ++++++ hyper-v.include.linux.hyperv.h ++++++ ++++ 972 lines (skipped) ++++ between /work/SRC/openSUSE:Factory/hyper-v/hyper-v.include.linux.hyperv.h ++++ and /work/SRC/openSUSE:Factory/.hyper-v.new.26871/hyper-v.include.linux.hyperv.h ++++++ hyper-v.tools.hv.hv_fcopy_uio_daemon.c ++++++ // SPDX-License-Identifier: GPL-2.0-only /* * An implementation of host to guest copy functionality for Linux. * * Copyright (C) 2023, Microsoft, Inc. * * Author : K. Y. Srinivasan <k...@microsoft.com> * Author : Saurabh Sengar <ssen...@microsoft.com> * */ #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <getopt.h> #include <locale.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <syslog.h> #include <unistd.h> #include <wchar.h> #include <sys/stat.h> #include <linux/hyperv.h> #include <linux/limits.h> #include "vmbus_bufring.h" #define ICMSGTYPE_NEGOTIATE 0 #define ICMSGTYPE_FCOPY 7 #define WIN8_SRV_MAJOR 1 #define WIN8_SRV_MINOR 1 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) #define MAX_FOLDER_NAME 15 #define MAX_PATH_LEN 15 #define FCOPY_UIO "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/uio" #define FCOPY_VER_COUNT 1 static const int fcopy_versions[] = { WIN8_SRV_VERSION }; #define FW_VER_COUNT 1 static const int fw_versions[] = { UTIL_FW_VERSION }; #define HV_RING_SIZE 0x4000 /* 16KB ring buffer size */ unsigned char desc[HV_RING_SIZE]; static int target_fd; static char target_fname[PATH_MAX]; static unsigned long long filesize; static int hv_fcopy_create_file(char *file_name, char *path_name, __u32 flags) { int error = HV_E_FAIL; char *q, *p; filesize = 0; p = path_name; snprintf(target_fname, sizeof(target_fname), "%s/%s", path_name, file_name); /* * Check to see if the path is already in place; if not, * create if required. */ while ((q = strchr(p, '/')) != NULL) { if (q == p) { p++; continue; } *q = '\0'; if (access(path_name, F_OK)) { if (flags & CREATE_PATH) { if (mkdir(path_name, 0755)) { syslog(LOG_ERR, "Failed to create %s", path_name); goto done; } } else { syslog(LOG_ERR, "Invalid path: %s", path_name); goto done; } } p = q + 1; *q = '/'; } if (!access(target_fname, F_OK)) { syslog(LOG_INFO, "File: %s exists", target_fname); if (!(flags & OVER_WRITE)) { error = HV_ERROR_ALREADY_EXISTS; goto done; } } target_fd = open(target_fname, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744); if (target_fd == -1) { syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); goto done; } error = 0; done: if (error) target_fname[0] = '\0'; return error; } /* copy the data into the file */ static int hv_copy_data(struct hv_do_fcopy *cpmsg) { ssize_t len; int ret = 0; len = pwrite(target_fd, cpmsg->data, cpmsg->size, cpmsg->offset); filesize += cpmsg->size; if (len != cpmsg->size) { switch (errno) { case ENOSPC: ret = HV_ERROR_DISK_FULL; break; default: ret = HV_E_FAIL; break; } syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)", filesize, (long)len, strerror(errno)); } return ret; } static int hv_copy_finished(void) { close(target_fd); target_fname[0] = '\0'; return 0; } static void print_usage(char *argv[]) { fprintf(stderr, "Usage: %s [options]\n" "Options are:\n" " -n, --no-daemon stay in foreground, don't daemonize\n" " -h, --help print this help\n", argv[0]); } static bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, unsigned char *buf, unsigned int buflen, const int *fw_version, int fw_vercnt, const int *srv_version, int srv_vercnt, int *nego_fw_version, int *nego_srv_version) { int icframe_major, icframe_minor; int icmsg_major, icmsg_minor; int fw_major, fw_minor; int srv_major, srv_minor; int i, j; bool found_match = false; struct icmsg_negotiate *negop; /* Check that there's enough space for icframe_vercnt, icmsg_vercnt */ if (buflen < ICMSG_HDR + offsetof(struct icmsg_negotiate, reserved)) { syslog(LOG_ERR, "Invalid icmsg negotiate"); return false; } icmsghdrp->icmsgsize = 0x10; negop = (struct icmsg_negotiate *)&buf[ICMSG_HDR]; icframe_major = negop->icframe_vercnt; icframe_minor = 0; icmsg_major = negop->icmsg_vercnt; icmsg_minor = 0; /* Validate negop packet */ if (icframe_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT || icmsg_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT || ICMSG_NEGOTIATE_PKT_SIZE(icframe_major, icmsg_major) > buflen) { syslog(LOG_ERR, "Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n", icframe_major, icmsg_major); goto fw_error; } /* * Select the framework version number we will * support. */ for (i = 0; i < fw_vercnt; i++) { fw_major = (fw_version[i] >> 16); fw_minor = (fw_version[i] & 0xFFFF); for (j = 0; j < negop->icframe_vercnt; j++) { if (negop->icversion_data[j].major == fw_major && negop->icversion_data[j].minor == fw_minor) { icframe_major = negop->icversion_data[j].major; icframe_minor = negop->icversion_data[j].minor; found_match = true; break; } } if (found_match) break; } if (!found_match) goto fw_error; found_match = false; for (i = 0; i < srv_vercnt; i++) { srv_major = (srv_version[i] >> 16); srv_minor = (srv_version[i] & 0xFFFF); for (j = negop->icframe_vercnt; (j < negop->icframe_vercnt + negop->icmsg_vercnt); j++) { if (negop->icversion_data[j].major == srv_major && negop->icversion_data[j].minor == srv_minor) { icmsg_major = negop->icversion_data[j].major; icmsg_minor = negop->icversion_data[j].minor; found_match = true; break; } } if (found_match) break; } /* * Respond with the framework and service * version numbers we can support. */ fw_error: if (!found_match) { negop->icframe_vercnt = 0; negop->icmsg_vercnt = 0; } else { negop->icframe_vercnt = 1; negop->icmsg_vercnt = 1; } if (nego_fw_version) *nego_fw_version = (icframe_major << 16) | icframe_minor; if (nego_srv_version) *nego_srv_version = (icmsg_major << 16) | icmsg_minor; negop->icversion_data[0].major = icframe_major; negop->icversion_data[0].minor = icframe_minor; negop->icversion_data[1].major = icmsg_major; negop->icversion_data[1].minor = icmsg_minor; return found_match; } static void wcstoutf8(char *dest, const __u16 *src, size_t dest_size) { size_t len = 0; while (len < dest_size) { if (src[len] < 0x80) dest[len++] = (char)(*src++); else dest[len++] = 'X'; } dest[len] = '\0'; } static int hv_fcopy_start(struct hv_start_fcopy *smsg_in) { setlocale(LC_ALL, "en_US.utf8"); size_t file_size, path_size; char *file_name, *path_name; char *in_file_name = (char *)smsg_in->file_name; char *in_path_name = (char *)smsg_in->path_name; file_size = wcstombs(NULL, (const wchar_t *restrict)in_file_name, 0) + 1; path_size = wcstombs(NULL, (const wchar_t *restrict)in_path_name, 0) + 1; file_name = (char *)malloc(file_size * sizeof(char)); path_name = (char *)malloc(path_size * sizeof(char)); if (!file_name || !path_name) { free(file_name); free(path_name); syslog(LOG_ERR, "Can't allocate memory for file name and/or path name"); return HV_E_FAIL; } wcstoutf8(file_name, (__u16 *)in_file_name, file_size); wcstoutf8(path_name, (__u16 *)in_path_name, path_size); return hv_fcopy_create_file(file_name, path_name, smsg_in->copy_flags); } static int hv_fcopy_send_data(struct hv_fcopy_hdr *fcopy_msg, int recvlen) { int operation = fcopy_msg->operation; /* * The strings sent from the host are encoded in * utf16; convert it to utf8 strings. * The host assures us that the utf16 strings will not exceed * the max lengths specified. We will however, reserve room * for the string terminating character - in the utf16s_utf8s() * function we limit the size of the buffer where the converted * string is placed to W_MAX_PATH -1 to guarantee * that the strings can be properly terminated! */ switch (operation) { case START_FILE_COPY: return hv_fcopy_start((struct hv_start_fcopy *)fcopy_msg); case WRITE_TO_FILE: return hv_copy_data((struct hv_do_fcopy *)fcopy_msg); case COMPLETE_FCOPY: return hv_copy_finished(); } return HV_E_FAIL; } /* process the packet recv from host */ static int fcopy_pkt_process(struct vmbus_br *txbr) { int ret, offset, pktlen; int fcopy_srv_version; const struct vmbus_chanpkt_hdr *pkt; struct hv_fcopy_hdr *fcopy_msg; struct icmsg_hdr *icmsghdr; pkt = (const struct vmbus_chanpkt_hdr *)desc; offset = pkt->hlen << 3; pktlen = (pkt->tlen << 3) - offset; icmsghdr = (struct icmsg_hdr *)&desc[offset + sizeof(struct vmbuspipe_hdr)]; icmsghdr->status = HV_E_FAIL; if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (vmbus_prep_negotiate_resp(icmsghdr, desc + offset, pktlen, fw_versions, FW_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT, NULL, &fcopy_srv_version)) { syslog(LOG_INFO, "FCopy IC version %d.%d", fcopy_srv_version >> 16, fcopy_srv_version & 0xFFFF); icmsghdr->status = 0; } } else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) { /* Ensure recvlen is big enough to contain hv_fcopy_hdr */ if (pktlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) { syslog(LOG_ERR, "Invalid Fcopy hdr. Packet length too small: %u", pktlen); return -ENOBUFS; } fcopy_msg = (struct hv_fcopy_hdr *)&desc[offset + ICMSG_HDR]; icmsghdr->status = hv_fcopy_send_data(fcopy_msg, pktlen); } icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; ret = rte_vmbus_chan_send(txbr, 0x6, desc + offset, pktlen, 0); if (ret) { syslog(LOG_ERR, "Write to ringbuffer failed err: %d", ret); return ret; } return 0; } static void fcopy_get_first_folder(char *path, char *chan_no) { DIR *dir = opendir(path); struct dirent *entry; if (!dir) { syslog(LOG_ERR, "Failed to open directory (errno=%s).\n", strerror(errno)); return; } while ((entry = readdir(dir)) != NULL) { if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { strcpy(chan_no, entry->d_name); break; } } closedir(dir); } int main(int argc, char *argv[]) { int fcopy_fd = -1, tmp = 1; int daemonize = 1, long_index = 0, opt, ret = -EINVAL; struct vmbus_br txbr, rxbr; void *ring; uint32_t len = HV_RING_SIZE; char uio_name[MAX_FOLDER_NAME] = {0}; char uio_dev_path[MAX_PATH_LEN] = {0}; static struct option long_options[] = { {"help", no_argument, 0, 'h' }, {"no-daemon", no_argument, 0, 'n' }, {0, 0, 0, 0 } }; while ((opt = getopt_long(argc, argv, "hn", long_options, &long_index)) != -1) { switch (opt) { case 'n': daemonize = 0; break; case 'h': default: print_usage(argv); goto exit; } } if (daemonize && daemon(1, 0)) { syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); goto exit; } openlog("HV_UIO_FCOPY", 0, LOG_USER); syslog(LOG_INFO, "starting; pid is:%d", getpid()); fcopy_get_first_folder(FCOPY_UIO, uio_name); snprintf(uio_dev_path, sizeof(uio_dev_path), "/dev/%s", uio_name); fcopy_fd = open(uio_dev_path, O_RDWR); if (fcopy_fd < 0) { syslog(LOG_ERR, "open %s failed; error: %d %s", uio_dev_path, errno, strerror(errno)); ret = fcopy_fd; goto exit; } ring = vmbus_uio_map(&fcopy_fd, HV_RING_SIZE); if (!ring) { ret = errno; syslog(LOG_ERR, "mmap ringbuffer failed; error: %d %s", ret, strerror(ret)); goto close; } vmbus_br_setup(&txbr, ring, HV_RING_SIZE); vmbus_br_setup(&rxbr, (char *)ring + HV_RING_SIZE, HV_RING_SIZE); rxbr.vbr->imask = 0; while (1) { /* * In this loop we process fcopy messages after the * handshake is complete. */ ret = pread(fcopy_fd, &tmp, sizeof(int), 0); if (ret < 0) { syslog(LOG_ERR, "pread failed: %s", strerror(errno)); continue; } len = HV_RING_SIZE; ret = rte_vmbus_chan_recv_raw(&rxbr, desc, &len); if (unlikely(ret <= 0)) { /* This indicates a failure to communicate (or worse) */ syslog(LOG_ERR, "VMBus channel recv error: %d", ret); } else { ret = fcopy_pkt_process(&txbr); if (ret < 0) goto close; /* Signal host */ if ((write(fcopy_fd, &tmp, sizeof(int))) != sizeof(int)) { ret = errno; syslog(LOG_ERR, "Signal to host failed: %s\n", strerror(ret)); goto close; } } } close: close(fcopy_fd); exit: return ret; } ++++++ hyper-v.tools.hv.hv_kvp_daemon.c ++++++ --- /var/tmp/diff_new_pack.NsqYXP/_old 2024-10-23 21:07:52.608708980 +0200 +++ /var/tmp/diff_new_pack.NsqYXP/_new 2024-10-23 21:07:52.612709146 +0200 @@ -44,7 +44,7 @@ /* * KVP protocol: The user mode component first registers with the - * the kernel component. Subsequently, the kernel component requests, data + * kernel component. Subsequently, the kernel component requests, data * for the specified keys. In response to this message the user mode component * fills in the value corresponding to the specified key. We overload the * sequence field in the cn_msg header to define our KVP message types. @@ -76,6 +76,12 @@ DNS }; +enum { + IPV4 = 1, + IPV6, + IP_TYPE_MAX +}; + static int in_hand_shake; static char *os_name = ""; @@ -102,6 +108,11 @@ #define MAX_FILE_NAME 100 #define ENTRIES_PER_BLOCK 50 +/* + * Change this entry if the number of addresses increases in future + */ +#define MAX_IP_ENTRIES 64 +#define OUTSTR_BUF_SIZE ((INET6_ADDRSTRLEN + 1) * MAX_IP_ENTRIES) struct kvp_record { char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; @@ -437,7 +448,7 @@ /* * Parse the /etc/os-release file if present: - * http://www.freedesktop.org/software/systemd/man/os-release.html + * https://www.freedesktop.org/software/systemd/man/os-release.html */ file = fopen("/etc/os-release", "r"); if (file != NULL) { @@ -772,11 +783,11 @@ const char *str; if (family == AF_INET) { - addr = (struct sockaddr_in *)addrp; + addr = addrp; str = inet_ntop(family, &addr->sin_addr, tmp, 50); addr_length = INET_ADDRSTRLEN; } else { - addr6 = (struct sockaddr_in6 *)addrp; + addr6 = addrp; str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50); addr_length = INET6_ADDRSTRLEN; } @@ -1171,11 +1182,156 @@ return 0; } +int ip_version_check(const char *input_addr) +{ + struct in6_addr addr; + + if (inet_pton(AF_INET, input_addr, &addr)) + return IPV4; + else if (inet_pton(AF_INET6, input_addr, &addr)) + return IPV6; + + return -EINVAL; +} + +/* + * Only IPv4 subnet strings needs to be converted to plen + * For IPv6 the subnet is already privided in plen format + */ +static int kvp_subnet_to_plen(char *subnet_addr_str) +{ + int plen = 0; + struct in_addr subnet_addr4; + + /* + * Convert subnet address to binary representation + */ + if (inet_pton(AF_INET, subnet_addr_str, &subnet_addr4) == 1) { + uint32_t subnet_mask = ntohl(subnet_addr4.s_addr); + + while (subnet_mask & 0x80000000) { + plen++; + subnet_mask <<= 1; + } + } else { + return -1; + } + + return plen; +} + +static int process_dns_gateway_nm(FILE *f, char *ip_string, int type, + int ip_sec) +{ + char addr[INET6_ADDRSTRLEN], *output_str; + int ip_offset = 0, error = 0, ip_ver; + char *param_name; + + if (type == DNS) + param_name = "dns"; + else if (type == GATEWAY) + param_name = "gateway"; + else + return -EINVAL; + + output_str = (char *)calloc(OUTSTR_BUF_SIZE, sizeof(char)); + if (!output_str) + return -ENOMEM; + + while (1) { + memset(addr, 0, sizeof(addr)); + + if (!parse_ip_val_buffer(ip_string, &ip_offset, addr, + (MAX_IP_ADDR_SIZE * 2))) + break; + + ip_ver = ip_version_check(addr); + if (ip_ver < 0) + continue; + + if ((ip_ver == IPV4 && ip_sec == IPV4) || + (ip_ver == IPV6 && ip_sec == IPV6)) { + /* + * do a bound check to avoid out-of bound writes + */ + if ((OUTSTR_BUF_SIZE - strlen(output_str)) > + (strlen(addr) + 1)) { + strncat(output_str, addr, + OUTSTR_BUF_SIZE - + strlen(output_str) - 1); + strncat(output_str, ",", + OUTSTR_BUF_SIZE - + strlen(output_str) - 1); + } + } else { + continue; + } + } + + if (strlen(output_str)) { + /* + * This is to get rid of that extra comma character + * in the end of the string + */ + output_str[strlen(output_str) - 1] = '\0'; + error = fprintf(f, "%s=%s\n", param_name, output_str); + } + + free(output_str); + return error; +} + +static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet, + int ip_sec) +{ + char addr[INET6_ADDRSTRLEN]; + char subnet_addr[INET6_ADDRSTRLEN]; + int error = 0, i = 0; + int ip_offset = 0, subnet_offset = 0; + int plen, ip_ver; + + memset(addr, 0, sizeof(addr)); + memset(subnet_addr, 0, sizeof(subnet_addr)); + + while (parse_ip_val_buffer(ip_string, &ip_offset, addr, + (MAX_IP_ADDR_SIZE * 2)) && + parse_ip_val_buffer(subnet, + &subnet_offset, + subnet_addr, + (MAX_IP_ADDR_SIZE * + 2))) { + ip_ver = ip_version_check(addr); + if (ip_ver < 0) + continue; + + if (ip_ver == IPV4 && ip_sec == IPV4) + plen = kvp_subnet_to_plen((char *)subnet_addr); + else if (ip_ver == IPV6 && ip_sec == IPV6) + plen = atoi(subnet_addr); + else + continue; + + if (plen < 0) + return plen; + + error = fprintf(f, "address%d=%s/%d\n", ++i, (char *)addr, + plen); + if (error < 0) + return error; + + memset(addr, 0, sizeof(addr)); + memset(subnet_addr, 0, sizeof(subnet_addr)); + } + + return error; +} + static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) { - int error = 0; - char if_file[PATH_MAX]; - FILE *file; + int error = 0, ip_ver; + char if_filename[PATH_MAX]; + char nm_filename[PATH_MAX]; + FILE *ifcfg_file, *nmfile; char cmd[PATH_MAX]; char *mac_addr; int str_len; @@ -1197,7 +1353,7 @@ * in a given distro to configure the interface and so are free * ignore information that may not be relevant. * - * Here is the format of the ip configuration file: + * Here is the ifcfg format of the ip configuration file: * * HWADDR=macaddr * DEVICE=interface name @@ -1220,6 +1376,32 @@ * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as * IPV6NETMASK. * + * Here is the keyfile format of the ip configuration file: + * + * [ethernet] + * mac-address=macaddr + * [connection] + * interface-name=interface name + * + * [ipv4] + * method=<protocol> (where <protocol> is "auto" if DHCP is configured + * or "manual" if no boot-time protocol should be used) + * + * address1=ipaddr1/plen + * address2=ipaddr2/plen + * + * gateway=gateway1;gateway2 + * + * dns=dns1;dns2 + * + * [ipv6] + * address1=ipaddr1/plen + * address2=ipaddr2/plen + * + * gateway=gateway1;gateway2 + * + * dns=dns1;dns2 + * * The host can specify multiple ipv4 and ipv6 addresses to be * configured for the interface. Furthermore, the configuration * needs to be persistent. A subsequent GET call on the interface @@ -1227,14 +1409,29 @@ * call. */ - snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, - "/ifcfg-", if_name); + /* + * We are populating both ifcfg and nmconnection files + */ + snprintf(if_filename, sizeof(if_filename), "%s%s%s", KVP_CONFIG_LOC, + "/ifcfg-", if_name); + + ifcfg_file = fopen(if_filename, "w"); + + if (!ifcfg_file) { + syslog(LOG_ERR, "Failed to open config file; error: %d %s", + errno, strerror(errno)); + return HV_E_FAIL; + } + + snprintf(nm_filename, sizeof(nm_filename), "%s%s%s%s", KVP_CONFIG_LOC, + "/", if_name, ".nmconnection"); - file = fopen(if_file, "w"); + nmfile = fopen(nm_filename, "w"); - if (file == NULL) { + if (!nmfile) { syslog(LOG_ERR, "Failed to open config file; error: %d %s", - errno, strerror(errno)); + errno, strerror(errno)); + fclose(ifcfg_file); return HV_E_FAIL; } @@ -1248,14 +1445,31 @@ goto setval_error; } - error = kvp_write_file(file, "HWADDR", "", mac_addr); - free(mac_addr); + error = kvp_write_file(ifcfg_file, "HWADDR", "", mac_addr); + if (error < 0) + goto setmac_error; + + error = kvp_write_file(ifcfg_file, "DEVICE", "", if_name); + if (error < 0) + goto setmac_error; + + error = fprintf(nmfile, "\n[connection]\n"); + if (error < 0) + goto setmac_error; + + error = kvp_write_file(nmfile, "interface-name", "", if_name); if (error) - goto setval_error; + goto setmac_error; + + error = fprintf(nmfile, "\n[ethernet]\n"); + if (error < 0) + goto setmac_error; - error = kvp_write_file(file, "DEVICE", "", if_name); + error = kvp_write_file(nmfile, "mac-address", "", mac_addr); if (error) - goto setval_error; + goto setmac_error; + + free(mac_addr); /* * The dhcp_enabled flag is only for IPv4. In the case the host only @@ -1263,47 +1477,137 @@ * proceed to parse and pass the IPv6 information to the * disto-specific script hv_set_ifconfig. */ + + /* + * First populate the ifcfg file format + */ if (new_val->dhcp_enabled) { - error = kvp_write_file(file, "BOOTPROTO", "", "dhcp"); + error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "dhcp"); if (error) goto setval_error; - } else { - error = kvp_write_file(file, "BOOTPROTO", "", "none"); + error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "none"); if (error) goto setval_error; } - /* - * Write the configuration for ipaddress, netmask, gateway and - * name servers. - */ - - error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR); + error = process_ip_string(ifcfg_file, (char *)new_val->ip_addr, + IPADDR); if (error) goto setval_error; - error = process_ip_string(file, (char *)new_val->sub_net, NETMASK); + error = process_ip_string(ifcfg_file, (char *)new_val->sub_net, + NETMASK); if (error) goto setval_error; - error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY); + error = process_ip_string(ifcfg_file, (char *)new_val->gate_way, + GATEWAY); if (error) goto setval_error; - error = process_ip_string(file, (char *)new_val->dns_addr, DNS); + error = process_ip_string(ifcfg_file, (char *)new_val->dns_addr, DNS); if (error) goto setval_error; - fclose(file); + /* + * Now we populate the keyfile format + * + * The keyfile format expects the IPv6 and IPv4 configuration in + * different sections. Therefore we iterate through the list twice, + * once to populate the IPv4 section and the next time for IPv6 + */ + ip_ver = IPV4; + do { + if (ip_ver == IPV4) { + error = fprintf(nmfile, "\n[ipv4]\n"); + if (error < 0) + goto setval_error; + } else { + error = fprintf(nmfile, "\n[ipv6]\n"); + if (error < 0) + goto setval_error; + } + + /* + * Write the configuration for ipaddress, netmask, gateway and + * name services + */ + error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr, + (char *)new_val->sub_net, + ip_ver); + if (error < 0) + goto setval_error; + + /* + * As dhcp_enabled is only valid for ipv4, we do not set dhcp + * methods for ipv6 based on dhcp_enabled flag. + * + * For ipv4, set method to manual only when dhcp_enabled is + * false and specific ipv4 addresses are configured. If neither + * dhcp_enabled is true and no ipv4 addresses are configured, + * set method to 'disabled'. + * + * For ipv6, set method to manual when we configure ipv6 + * addresses. Otherwise set method to 'auto' so that SLAAC from + * RA may be used. + */ + if (ip_ver == IPV4) { + if (new_val->dhcp_enabled) { + error = kvp_write_file(nmfile, "method", "", + "auto"); + if (error < 0) + goto setval_error; + } else if (error) { + error = kvp_write_file(nmfile, "method", "", + "manual"); + if (error < 0) + goto setval_error; + } else { + error = kvp_write_file(nmfile, "method", "", + "disabled"); + if (error < 0) + goto setval_error; + } + } else if (ip_ver == IPV6) { + if (error) { + error = kvp_write_file(nmfile, "method", "", + "manual"); + if (error < 0) + goto setval_error; + } else { + error = kvp_write_file(nmfile, "method", "", + "auto"); + if (error < 0) + goto setval_error; + } + } + + error = process_dns_gateway_nm(nmfile, + (char *)new_val->gate_way, + GATEWAY, ip_ver); + if (error < 0) + goto setval_error; + + error = process_dns_gateway_nm(nmfile, + (char *)new_val->dns_addr, DNS, + ip_ver); + if (error < 0) + goto setval_error; + + ip_ver++; + } while (ip_ver < IP_TYPE_MAX); + + fclose(nmfile); + fclose(ifcfg_file); /* * Now that we have populated the configuration file, * invoke the external script to do its magic. */ - str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s", - "hv_set_ifconfig", if_file); + str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s %s", + "hv_set_ifconfig", if_filename, nm_filename); /* * This is a little overcautious, but it's necessary to suppress some * false warnings from gcc 8.0.1. @@ -1321,10 +1625,13 @@ } return 0; +setmac_error: + free(mac_addr); setval_error: syslog(LOG_ERR, "Failed to write config file. error: %d %s", errno, strerror(errno)); - fclose(file); + fclose(ifcfg_file); + fclose(nmfile); return error; } ++++++ hyper-v.tools.hv.hv_set_ifconfig.sh ++++++ --- /var/tmp/diff_new_pack.NsqYXP/_old 2024-10-23 21:07:52.632709976 +0200 +++ /var/tmp/diff_new_pack.NsqYXP/_new 2024-10-23 21:07:52.636710143 +0200 @@ -7,12 +7,12 @@ # The only argument to this script is the configuration file that is to # be used to configure the interface. # -# Here is the format of the ip configuration file: +# Here is the ifcfg format of the ip configuration file: # # HWADDR=macaddr # DEVICE=interface name # BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured -# or "none" if no boot-time protocol should be used) +# or "none" if no boot-time protocol should be used) # # IPADDR0=ipaddr1 # IPADDR1=ipaddr2 @@ -30,14 +30,41 @@ # tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as # IPV6NETMASK. # +# Here is the keyfile format of the ip configuration file: +# +# [ethernet] +# mac-address=macaddr +# [connection] +# interface-name=interface name +# +# [ipv4] +# method=<protocol> (where <protocol> is "auto" if DHCP is configured +# or "manual" if no boot-time protocol should be used) +# +# address1=ipaddr1/plen +# address2=ipaddr2/plen +# +# gateway=gateway1;gateway2 +# +# dns=dns1; +# +# [ipv6] +# address1=ipaddr1/plen +# address2=ipaddr2/plen +# +# gateway=gateway1;gateway2 +# +# dns=dns1;dns2 +# # The host can specify multiple ipv4 and ipv6 addresses to be # configured for the interface. Furthermore, the configuration # needs to be persistent. A subsequent GET call on the interface # is expected to return the configuration that is set via the SET # call. # -cfg=$1 -if ! test -f "${cfg}" +if_cfg=$1 +nm_cfg=$2 +if ! test -f "${if_cfg}" then : expect configuration datafile as first argument exit 1 @@ -86,11 +113,11 @@ unset ${!IPV6NETMASK*} unset ${!IPV6_DEFAULTGW*} unset ${!DNS*} -. "$1" +. "${if_cfg}" # if test -z "${DEVICE}" then - echo "Missing DEVICE= in ${cfg}" + echo "Missing DEVICE= in ${if_cfg}" exit 1 fi # @@ -200,6 +227,7 @@ done # echo "$0: working on network interface ifcfg-${DEVICE}" +mkdir -vp "/etc/sysconfig/network" cp -fb ${t_ifcfg} "/etc/sysconfig/network/ifcfg-${DEVICE}" cp -fb ${t_ifroute} "/etc/sysconfig/network/ifroute-${DEVICE}" if test -w /etc/sysconfig/network/config @@ -207,6 +235,13 @@ sed -i "s@^NETCONFIG_DNS_STATIC_SERVERS=.*@NETCONFIG_DNS_STATIC_SERVERS='$_DNS_'@" /etc/sysconfig/network/config netconfig update -m dns fi + +echo "$0: working on network interface ifcfg-${DEVICE}" +nm_filename="${nm_cfg##*/}" +mkdir -vp "/etc/NetworkManager/system-connections" +umask 0177 +sed '/\[connection\]/a autoconnect=true' "${nm_cfg}" > "/etc/NetworkManager/system-connections/${nm_filename}" + ifdown "${DEVICE}" ifup "${DEVICE}" ) 2>&1 | logger -t "${0##*/}[$PPID / $$]" ++++++ hyper-v.tools.hv.vmbus_bufring.c ++++++ // SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2009-2012,2016,2023 Microsoft Corp. * Copyright (c) 2012 NetApp Inc. * Copyright (c) 2012 Citrix Inc. * All rights reserved. */ #include <errno.h> #include <fcntl.h> #include <emmintrin.h> #include <linux/limits.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include <sys/mman.h> #include <sys/uio.h> #include <unistd.h> #include "vmbus_bufring.h" /** * Compiler barrier. * * Guarantees that operation reordering does not occur at compile time * for operations directly before and after the barrier. */ #define rte_compiler_barrier() ({ asm volatile ("" : : : "memory"); }) #define VMBUS_RQST_ERROR 0xFFFFFFFFFFFFFFFF #define ALIGN(val, align) ((typeof(val))((val) & (~((typeof(val))((align) - 1))))) void *vmbus_uio_map(int *fd, int size) { void *map; map = mmap(NULL, 2 * size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0); if (map == MAP_FAILED) return NULL; return map; } /* Increase bufring index by inc with wraparound */ static inline uint32_t vmbus_br_idxinc(uint32_t idx, uint32_t inc, uint32_t sz) { idx += inc; if (idx >= sz) idx -= sz; return idx; } void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen) { br->vbr = buf; br->windex = br->vbr->windex; br->dsize = blen - sizeof(struct vmbus_bufring); } static inline __always_inline void rte_smp_mb(void) { asm volatile("lock addl $0, -128(%%rsp); " ::: "memory"); } static inline int rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src) { uint8_t res; asm volatile("lock ; " "cmpxchgl %[src], %[dst];" "sete %[res];" : [res] "=a" (res), /* output */ [dst] "=m" (*dst) : [src] "r" (src), /* input */ "a" (exp), "m" (*dst) : "memory"); /* no-clobber list */ return res; } static inline uint32_t vmbus_txbr_copyto(const struct vmbus_br *tbr, uint32_t windex, const void *src0, uint32_t cplen) { uint8_t *br_data = tbr->vbr->data; uint32_t br_dsize = tbr->dsize; const uint8_t *src = src0; /* XXX use double mapping like Linux kernel? */ if (cplen > br_dsize - windex) { uint32_t fraglen = br_dsize - windex; /* Wrap-around detected */ memcpy(br_data + windex, src, fraglen); memcpy(br_data, src + fraglen, cplen - fraglen); } else { memcpy(br_data + windex, src, cplen); } return vmbus_br_idxinc(windex, cplen, br_dsize); } /* * Write scattered channel packet to TX bufring. * * The offset of this channel packet is written as a 64bits value * immediately after this channel packet. * * The write goes through three stages: * 1. Reserve space in ring buffer for the new data. * Writer atomically moves priv_write_index. * 2. Copy the new data into the ring. * 3. Update the tail of the ring (visible to host) that indicates * next read location. Writer updates write_index */ static int vmbus_txbr_write(struct vmbus_br *tbr, const struct iovec iov[], int iovlen) { struct vmbus_bufring *vbr = tbr->vbr; uint32_t ring_size = tbr->dsize; uint32_t old_windex, next_windex, windex, total; uint64_t save_windex; int i; total = 0; for (i = 0; i < iovlen; i++) total += iov[i].iov_len; total += sizeof(save_windex); /* Reserve space in ring */ do { uint32_t avail; /* Get current free location */ old_windex = tbr->windex; /* Prevent compiler reordering this with calculation */ rte_compiler_barrier(); avail = vmbus_br_availwrite(tbr, old_windex); /* If not enough space in ring, then tell caller. */ if (avail <= total) return -EAGAIN; next_windex = vmbus_br_idxinc(old_windex, total, ring_size); /* Atomic update of next write_index for other threads */ } while (!rte_atomic32_cmpset(&tbr->windex, old_windex, next_windex)); /* Space from old..new is now reserved */ windex = old_windex; for (i = 0; i < iovlen; i++) windex = vmbus_txbr_copyto(tbr, windex, iov[i].iov_base, iov[i].iov_len); /* Set the offset of the current channel packet. */ save_windex = ((uint64_t)old_windex) << 32; windex = vmbus_txbr_copyto(tbr, windex, &save_windex, sizeof(save_windex)); /* The region reserved should match region used */ if (windex != next_windex) return -EINVAL; /* Ensure that data is available before updating host index */ rte_compiler_barrier(); /* Checkin for our reservation. wait for our turn to update host */ while (!rte_atomic32_cmpset(&vbr->windex, old_windex, next_windex)) _mm_pause(); return 0; } int rte_vmbus_chan_send(struct vmbus_br *txbr, uint16_t type, void *data, uint32_t dlen, uint32_t flags) { struct vmbus_chanpkt pkt; unsigned int pktlen, pad_pktlen; const uint32_t hlen = sizeof(pkt); uint64_t pad = 0; struct iovec iov[3]; int error; pktlen = hlen + dlen; pad_pktlen = ALIGN(pktlen, sizeof(uint64_t)); pkt.hdr.type = type; pkt.hdr.flags = flags; pkt.hdr.hlen = hlen >> VMBUS_CHANPKT_SIZE_SHIFT; pkt.hdr.tlen = pad_pktlen >> VMBUS_CHANPKT_SIZE_SHIFT; pkt.hdr.xactid = VMBUS_RQST_ERROR; iov[0].iov_base = &pkt; iov[0].iov_len = hlen; iov[1].iov_base = data; iov[1].iov_len = dlen; iov[2].iov_base = &pad; iov[2].iov_len = pad_pktlen - pktlen; error = vmbus_txbr_write(txbr, iov, 3); return error; } static inline uint32_t vmbus_rxbr_copyfrom(const struct vmbus_br *rbr, uint32_t rindex, void *dst0, size_t cplen) { const uint8_t *br_data = rbr->vbr->data; uint32_t br_dsize = rbr->dsize; uint8_t *dst = dst0; if (cplen > br_dsize - rindex) { uint32_t fraglen = br_dsize - rindex; /* Wrap-around detected. */ memcpy(dst, br_data + rindex, fraglen); memcpy(dst + fraglen, br_data, cplen - fraglen); } else { memcpy(dst, br_data + rindex, cplen); } return vmbus_br_idxinc(rindex, cplen, br_dsize); } /* Copy data from receive ring but don't change index */ static int vmbus_rxbr_peek(const struct vmbus_br *rbr, void *data, size_t dlen) { uint32_t avail; /* * The requested data and the 64bits channel packet * offset should be there at least. */ avail = vmbus_br_availread(rbr); if (avail < dlen + sizeof(uint64_t)) return -EAGAIN; vmbus_rxbr_copyfrom(rbr, rbr->vbr->rindex, data, dlen); return 0; } /* * Copy data from receive ring and change index * NOTE: * We assume (dlen + skip) == sizeof(channel packet). */ static int vmbus_rxbr_read(struct vmbus_br *rbr, void *data, size_t dlen, size_t skip) { struct vmbus_bufring *vbr = rbr->vbr; uint32_t br_dsize = rbr->dsize; uint32_t rindex; if (vmbus_br_availread(rbr) < dlen + skip + sizeof(uint64_t)) return -EAGAIN; /* Record where host was when we started read (for debug) */ rbr->windex = rbr->vbr->windex; /* * Copy channel packet from RX bufring. */ rindex = vmbus_br_idxinc(rbr->vbr->rindex, skip, br_dsize); rindex = vmbus_rxbr_copyfrom(rbr, rindex, data, dlen); /* * Discard this channel packet's 64bits offset, which is useless to us. */ rindex = vmbus_br_idxinc(rindex, sizeof(uint64_t), br_dsize); /* Update the read index _after_ the channel packet is fetched. */ rte_compiler_barrier(); vbr->rindex = rindex; return 0; } int rte_vmbus_chan_recv_raw(struct vmbus_br *rxbr, void *data, uint32_t *len) { struct vmbus_chanpkt_hdr pkt; uint32_t dlen, bufferlen = *len; int error; error = vmbus_rxbr_peek(rxbr, &pkt, sizeof(pkt)); if (error) return error; if (unlikely(pkt.hlen < VMBUS_CHANPKT_HLEN_MIN)) /* XXX this channel is dead actually. */ return -EIO; if (unlikely(pkt.hlen > pkt.tlen)) return -EIO; /* Length are in quad words */ dlen = pkt.tlen << VMBUS_CHANPKT_SIZE_SHIFT; *len = dlen; /* If caller buffer is not large enough */ if (unlikely(dlen > bufferlen)) return -ENOBUFS; /* Read data and skip packet header */ error = vmbus_rxbr_read(rxbr, data, dlen, 0); if (error) return error; /* Return the number of bytes read */ return dlen + sizeof(uint64_t); } ++++++ hyper-v.tools.hv.vmbus_bufring.h ++++++ /* SPDX-License-Identifier: BSD-3-Clause */ #ifndef _VMBUS_BUF_H_ #define _VMBUS_BUF_H_ #include <stdbool.h> #include <stdint.h> #define __packed __attribute__((__packed__)) #define unlikely(x) __builtin_expect(!!(x), 0) #define ICMSGHDRFLAG_TRANSACTION 1 #define ICMSGHDRFLAG_REQUEST 2 #define ICMSGHDRFLAG_RESPONSE 4 #define IC_VERSION_NEGOTIATION_MAX_VER_COUNT 100 #define ICMSG_HDR (sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)) #define ICMSG_NEGOTIATE_PKT_SIZE(icframe_vercnt, icmsg_vercnt) \ (ICMSG_HDR + sizeof(struct icmsg_negotiate) + \ (((icframe_vercnt) + (icmsg_vercnt)) * sizeof(struct ic_version))) /* * Channel packets */ /* Channel packet flags */ #define VMBUS_CHANPKT_TYPE_INBAND 0x0006 #define VMBUS_CHANPKT_TYPE_RXBUF 0x0007 #define VMBUS_CHANPKT_TYPE_GPA 0x0009 #define VMBUS_CHANPKT_TYPE_COMP 0x000b #define VMBUS_CHANPKT_FLAG_NONE 0 #define VMBUS_CHANPKT_FLAG_RC 0x0001 /* report completion */ #define VMBUS_CHANPKT_SIZE_SHIFT 3 #define VMBUS_CHANPKT_SIZE_ALIGN BIT(VMBUS_CHANPKT_SIZE_SHIFT) #define VMBUS_CHANPKT_HLEN_MIN \ (sizeof(struct vmbus_chanpkt_hdr) >> VMBUS_CHANPKT_SIZE_SHIFT) /* * Buffer ring */ struct vmbus_bufring { volatile uint32_t windex; volatile uint32_t rindex; /* * Interrupt mask {0,1} * * For TX bufring, host set this to 1, when it is processing * the TX bufring, so that we can safely skip the TX event * notification to host. * * For RX bufring, once this is set to 1 by us, host will not * further dispatch interrupts to us, even if there are data * pending on the RX bufring. This effectively disables the * interrupt of the channel to which this RX bufring is attached. */ volatile uint32_t imask; /* * Win8 uses some of the reserved bits to implement * interrupt driven flow management. On the send side * we can request that the receiver interrupt the sender * when the ring transitions from being full to being able * to handle a message of size "pending_send_sz". * * Add necessary state for this enhancement. */ volatile uint32_t pending_send; uint32_t reserved1[12]; union { struct { uint32_t feat_pending_send_sz:1; }; uint32_t value; } feature_bits; /* Pad it to rte_mem_page_size() so that data starts on page boundary */ uint8_t reserved2[4028]; /* * Ring data starts here + RingDataStartOffset * !!! DO NOT place any fields below this !!! */ uint8_t data[]; } __packed; struct vmbus_br { struct vmbus_bufring *vbr; uint32_t dsize; uint32_t windex; /* next available location */ }; struct vmbus_chanpkt_hdr { uint16_t type; /* VMBUS_CHANPKT_TYPE_ */ uint16_t hlen; /* header len, in 8 bytes */ uint16_t tlen; /* total len, in 8 bytes */ uint16_t flags; /* VMBUS_CHANPKT_FLAG_ */ uint64_t xactid; } __packed; struct vmbus_chanpkt { struct vmbus_chanpkt_hdr hdr; } __packed; struct vmbuspipe_hdr { unsigned int flags; unsigned int msgsize; } __packed; struct ic_version { unsigned short major; unsigned short minor; } __packed; struct icmsg_negotiate { unsigned short icframe_vercnt; unsigned short icmsg_vercnt; unsigned int reserved; struct ic_version icversion_data[]; /* any size array */ } __packed; struct icmsg_hdr { struct ic_version icverframe; unsigned short icmsgtype; struct ic_version icvermsg; unsigned short icmsgsize; unsigned int status; unsigned char ictransaction_id; unsigned char icflags; unsigned char reserved[2]; } __packed; int rte_vmbus_chan_recv_raw(struct vmbus_br *rxbr, void *data, uint32_t *len); int rte_vmbus_chan_send(struct vmbus_br *txbr, uint16_t type, void *data, uint32_t dlen, uint32_t flags); void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen); void *vmbus_uio_map(int *fd, int size); /* Amount of space available for write */ static inline uint32_t vmbus_br_availwrite(const struct vmbus_br *br, uint32_t windex) { uint32_t rindex = br->vbr->rindex; if (windex >= rindex) return br->dsize - (windex - rindex); else return rindex - windex; } static inline uint32_t vmbus_br_availread(const struct vmbus_br *br) { return br->dsize - vmbus_br_availwrite(br, br->vbr->windex); } #endif /* !_VMBUS_BUF_H_ */