hyperv-fixes for v6.6-rc6

-----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCgAxFiEEIbPD0id6easf0xsudhRwX5BBoF4FAmUk4fcTHHdlaS5saXVA
 a2VybmVsLm9yZwAKCRB2FHBfkEGgXhhqCACWsBYTB0EJ3oMJnzfnHeuN418ZDx/O
 AL0k0O5MT6roEFmvGUhzJ/jsoxL+W+Wj3aFwzReyOSQpgjTTF/Ja26LPvxRzDxKi
 sZPojnR2ykW31l7y+eh1p9qSM/aYvTMDP5zO7L1fBnWMAGMv8w8RezpCJ7bh4BgA
 FTMZZrvKYVT9hCGkYqKUZGBtDTPZ56WE+MCiRxTWQvF+4QKaIff0tpno8V7203bE
 D/b4+Ouh19RXFTC5dUq/0JtAdV2AadrPHnScUupc8Hk/MMFiU5CzvH4bAqiwXBcU
 YqqlD3kZbIqqbKE93+03jvyrRDvDGlq+rpA3KMk5MBAfrkM4DytpWvMs
 =SVq1
 -----END PGP SIGNATURE-----

Merge tag 'hyperv-fixes-signed-20231009' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux

Pull hyperv fixes from Wei Liu:

 - fixes for Hyper-V VTL code (Saurabh Sengar and Olaf Hering)

 - fix hv_kvp_daemon to support keyfile based connection profile
   (Shradha Gupta)

* tag 'hyperv-fixes-signed-20231009' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux:
  hv/hv_kvp_daemon:Support for keyfile based connection profile
  hyperv: reduce size of ms_hyperv_info
  x86/hyperv: Add common print prefix "Hyper-V" in hv_init
  x86/hyperv: Remove hv_vtl_early_init initcall
  x86/hyperv: Restrict get_vtl to only VTL platforms
This commit is contained in:
Linus Torvalds 2023-10-10 11:01:21 -07:00
commit b711538a40
6 changed files with 257 additions and 50 deletions

View file

@ -7,6 +7,8 @@
* Author : K. Y. Srinivasan <kys@microsoft.com>
*/
#define pr_fmt(fmt) "Hyper-V: " fmt
#include <linux/efi.h>
#include <linux/types.h>
#include <linux/bitfield.h>
@ -191,7 +193,7 @@ void set_hv_tscchange_cb(void (*cb)(void))
struct hv_tsc_emulation_control emu_ctrl = {.enabled = 1};
if (!hv_reenlightenment_available()) {
pr_warn("Hyper-V: reenlightenment support is unavailable\n");
pr_warn("reenlightenment support is unavailable\n");
return;
}
@ -394,6 +396,7 @@ static void __init hv_get_partition_id(void)
local_irq_restore(flags);
}
#if IS_ENABLED(CONFIG_HYPERV_VTL_MODE)
static u8 __init get_vtl(void)
{
u64 control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_REGISTERS;
@ -416,13 +419,16 @@ static u8 __init get_vtl(void)
if (hv_result_success(ret)) {
ret = output->as64.low & HV_X64_VTL_MASK;
} else {
pr_err("Failed to get VTL(%lld) and set VTL to zero by default.\n", ret);
ret = 0;
pr_err("Failed to get VTL(error: %lld) exiting...\n", ret);
BUG();
}
local_irq_restore(flags);
return ret;
}
#else
static inline u8 get_vtl(void) { return 0; }
#endif
/*
* This function is to be invoked early in the boot sequence after the
@ -564,7 +570,7 @@ void __init hyperv_init(void)
if (cpu_feature_enabled(X86_FEATURE_IBT) &&
*(u32 *)hv_hypercall_pg != gen_endbr()) {
setup_clear_cpu_cap(X86_FEATURE_IBT);
pr_warn("Hyper-V: Disabling IBT because of Hyper-V bug\n");
pr_warn("Disabling IBT because of Hyper-V bug\n");
}
#endif
@ -604,8 +610,10 @@ void __init hyperv_init(void)
hv_query_ext_cap(0);
/* Find the VTL */
if (!ms_hyperv.paravisor_present && hv_isolation_type_snp())
ms_hyperv.vtl = get_vtl();
ms_hyperv.vtl = get_vtl();
if (ms_hyperv.vtl > 0) /* non default VTL */
hv_vtl_early_init();
return;

View file

@ -215,7 +215,7 @@ static int hv_vtl_wakeup_secondary_cpu(int apicid, unsigned long start_eip)
return hv_vtl_bringup_vcpu(vp_id, start_eip);
}
static int __init hv_vtl_early_init(void)
int __init hv_vtl_early_init(void)
{
/*
* `boot_cpu_has` returns the runtime feature support,
@ -230,4 +230,3 @@ static int __init hv_vtl_early_init(void)
return 0;
}
early_initcall(hv_vtl_early_init);

View file

@ -340,8 +340,10 @@ static inline u64 hv_get_non_nested_register(unsigned int reg) { return 0; }
#ifdef CONFIG_HYPERV_VTL_MODE
void __init hv_vtl_init_platform(void);
int __init hv_vtl_early_init(void);
#else
static inline void __init hv_vtl_init_platform(void) {}
static inline int __init hv_vtl_early_init(void) { return 0; }
#endif
#include <asm-generic/mshyperv.h>

View file

@ -36,6 +36,7 @@ struct ms_hyperv_info {
u32 nested_features;
u32 max_vp_index;
u32 max_lp_index;
u8 vtl;
union {
u32 isolation_config_a;
struct {
@ -54,7 +55,6 @@ struct ms_hyperv_info {
};
};
u64 shared_gpa_boundary;
u8 vtl;
};
extern struct ms_hyperv_info ms_hyperv;
extern bool hv_nested;

View file

@ -1171,12 +1171,79 @@ static int process_ip_string(FILE *f, char *ip_string, int type)
return 0;
}
/*
* 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_ip_string_nm(FILE *f, char *ip_string, char *subnet,
int is_ipv6)
{
char addr[INET6_ADDRSTRLEN];
char subnet_addr[INET6_ADDRSTRLEN];
int error, i = 0;
int ip_offset = 0, subnet_offset = 0;
int plen;
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))) {
if (!is_ipv6)
plen = kvp_subnet_to_plen((char *)subnet_addr);
else
plen = atoi(subnet_addr);
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 0;
}
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;
char if_filename[PATH_MAX];
char nm_filename[PATH_MAX];
FILE *ifcfg_file, *nmfile;
char cmd[PATH_MAX];
int is_ipv6 = 0;
char *mac_addr;
int str_len;
@ -1197,7 +1264,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
* 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 +1287,32 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
* 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 +1320,29 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
* 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);
file = fopen(if_file, "w");
ifcfg_file = fopen(if_filename, "w");
if (file == NULL) {
if (!ifcfg_file) {
syslog(LOG_ERR, "Failed to open config file; error: %d %s",
errno, strerror(errno));
errno, strerror(errno));
return HV_E_FAIL;
}
snprintf(nm_filename, sizeof(nm_filename), "%s%s%s%s", KVP_CONFIG_LOC,
"/", if_name, ".nmconnection");
nmfile = fopen(nm_filename, "w");
if (!nmfile) {
syslog(LOG_ERR, "Failed to open config file; error: %d %s",
errno, strerror(errno));
fclose(ifcfg_file);
return HV_E_FAIL;
}
@ -1248,14 +1356,31 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
goto setval_error;
}
error = kvp_write_file(file, "HWADDR", "", mac_addr);
free(mac_addr);
if (error)
goto setval_error;
error = kvp_write_file(ifcfg_file, "HWADDR", "", mac_addr);
if (error < 0)
goto setmac_error;
error = kvp_write_file(file, "DEVICE", "", if_name);
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(nmfile, "mac-address", "", mac_addr);
if (error)
goto setmac_error;
free(mac_addr);
/*
* The dhcp_enabled flag is only for IPv4. In the case the host only
@ -1263,47 +1388,91 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
* 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;
}
error = process_ip_string(ifcfg_file, (char *)new_val->ip_addr,
IPADDR);
if (error)
goto setval_error;
error = process_ip_string(ifcfg_file, (char *)new_val->sub_net,
NETMASK);
if (error)
goto setval_error;
error = process_ip_string(ifcfg_file, (char *)new_val->gate_way,
GATEWAY);
if (error)
goto setval_error;
error = process_ip_string(ifcfg_file, (char *)new_val->dns_addr, DNS);
if (error)
goto setval_error;
if (new_val->addr_family == ADDR_FAMILY_IPV6) {
error = fprintf(nmfile, "\n[ipv6]\n");
if (error < 0)
goto setval_error;
is_ipv6 = 1;
} else {
error = fprintf(nmfile, "\n[ipv4]\n");
if (error < 0)
goto setval_error;
}
/*
* Now we populate the keyfile format
*/
if (new_val->dhcp_enabled) {
error = kvp_write_file(nmfile, "method", "", "auto");
if (error < 0)
goto setval_error;
} else {
error = kvp_write_file(nmfile, "method", "", "manual");
if (error < 0)
goto setval_error;
}
/*
* Write the configuration for ipaddress, netmask, gateway and
* name servers.
* name services
*/
error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR);
if (error)
error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr,
(char *)new_val->sub_net, is_ipv6);
if (error < 0)
goto setval_error;
error = process_ip_string(file, (char *)new_val->sub_net, NETMASK);
if (error)
error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way);
if (error < 0)
goto setval_error;
error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY);
if (error)
error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr);
if (error < 0)
goto setval_error;
error = process_ip_string(file, (char *)new_val->dns_addr, DNS);
if (error)
goto setval_error;
fclose(file);
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.
@ -1316,14 +1485,16 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
if (system(cmd)) {
syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s",
cmd, errno, strerror(errno));
cmd, errno, strerror(errno));
return HV_E_FAIL;
}
return 0;
setmac_error:
free(mac_addr);
setval_error:
syslog(LOG_ERR, "Failed to write config file");
fclose(file);
fclose(ifcfg_file);
fclose(nmfile);
return error;
}

View file

@ -18,12 +18,12 @@
#
# This example script is based on a RHEL environment.
#
# 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
@ -41,6 +41,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
# address=ipaddr2/plen
#
# gateway=gateway1;gateway2
#
# dns=dns1;
#
# [ipv6]
# address1=ipaddr1/plen
# address2=ipaddr1/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
@ -48,18 +74,19 @@
# call.
#
echo "IPV6INIT=yes" >> $1
echo "NM_CONTROLLED=no" >> $1
echo "PEERDNS=yes" >> $1
echo "ONBOOT=yes" >> $1
cp $1 /etc/sysconfig/network-scripts/
chmod 600 $2
interface=$(echo $2 | awk -F - '{ print $2 }')
filename="${2##*/}"
sed '/\[connection\]/a autoconnect=true' $2 > /etc/NetworkManager/system-connections/${filename}
interface=$(echo $1 | awk -F - '{ print $2 }')
/sbin/ifdown $interface 2>/dev/null
/sbin/ifup $interface 2>/dev/null