Adds NIC hardware offload support
Adds get_nic_hardware_offload_info method in hostutils, which will return a list of dictionaries containing details regarding different NIC hardware offload capacities for SR-IOV, IOV queue pairs, VMQs, IPsec SAs. Adds set_vswitch_port_offload method in networkutils, which will set the given hardware offload options into the given switch port (enable / disable SR-IOV or VMQ, set IOV queues, set offloaded IPsec SAs). Implements: blueprint nic-hardware-offload Change-Id: Ib2915bd4fe050b97597799f359b310eef7457c58
This commit is contained in:
parent
bbaa712277
commit
63ccd40ed9
@ -653,7 +653,7 @@ class NetworkUtilsTestCase(test_base.OsWinBaseTestCase):
|
||||
def test_set_vswitch_port_sriov_already_set(self, mock_get_hw_offload_sd):
|
||||
mock_port_alloc = self._mock_get_switch_port_alloc()
|
||||
mock_hw_offload_sd = mock_get_hw_offload_sd.return_value
|
||||
mock_hw_offload_sd.IOVOffloadWeight = self.netutils._IOV_ENABLED
|
||||
mock_hw_offload_sd.IOVOffloadWeight = self.netutils._OFFLOAD_ENABLED
|
||||
|
||||
self.netutils.set_vswitch_port_sriov(mock.sentinel.port_name,
|
||||
True)
|
||||
@ -674,10 +674,51 @@ class NetworkUtilsTestCase(test_base.OsWinBaseTestCase):
|
||||
mock_get_hw_offload_sd.assert_called_once_with(mock_port_alloc)
|
||||
self.netutils._jobutils.modify_virt_feature.assert_called_with(
|
||||
mock_hw_offload_sd)
|
||||
desired_state = (self.netutils._IOV_ENABLED if state else
|
||||
self.netutils._IOV_DISABLED)
|
||||
desired_state = (self.netutils._OFFLOAD_ENABLED if state else
|
||||
self.netutils._OFFLOAD_DISABLED)
|
||||
self.assertEqual(desired_state, mock_hw_offload_sd.IOVOffloadWeight)
|
||||
|
||||
@ddt.data({'iov_queues_requested': 0},
|
||||
{'offloaded_sa': 0})
|
||||
@ddt.unpack
|
||||
def test_set_vswitch_port_offload_invalid(self, iov_queues_requested=1,
|
||||
offloaded_sa=1024):
|
||||
self.assertRaises(exceptions.InvalidParameterValue,
|
||||
self.netutils.set_vswitch_port_offload,
|
||||
mock.sentinel.port_name,
|
||||
iov_queues_requested=iov_queues_requested,
|
||||
offloaded_sa=offloaded_sa)
|
||||
|
||||
@mock.patch.object(networkutils.NetworkUtils,
|
||||
'_get_hw_offload_sd_from_port_alloc')
|
||||
def test_set_vswitch_port_offload_noop(self, mock_get_hw_offload_sd):
|
||||
self._mock_get_switch_port_alloc()
|
||||
self.netutils.set_vswitch_port_offload(mock.sentinel.port_name)
|
||||
self.netutils._jobutils.modify_virt_feature.assert_not_called()
|
||||
|
||||
@mock.patch.object(networkutils.NetworkUtils,
|
||||
'_get_hw_offload_sd_from_port_alloc')
|
||||
def test_set_vswitch_port_offload(self, mock_get_hw_offload_sd):
|
||||
mock_port_alloc = self._mock_get_switch_port_alloc()
|
||||
mock_hw_offload_sd = mock_get_hw_offload_sd.return_value
|
||||
iov_queues = 1
|
||||
offloaded_sa = 1
|
||||
|
||||
self.netutils.set_vswitch_port_offload(
|
||||
mock.sentinel.port_name, True, iov_queues, True, offloaded_sa)
|
||||
|
||||
mock_get_hw_offload_sd.assert_called_once_with(mock_port_alloc)
|
||||
self.netutils._jobutils.modify_virt_feature.assert_called_with(
|
||||
mock_hw_offload_sd)
|
||||
self.assertEqual(self.netutils._OFFLOAD_ENABLED,
|
||||
mock_hw_offload_sd.IOVOffloadWeight)
|
||||
self.assertEqual(iov_queues,
|
||||
mock_hw_offload_sd.IOVQueuePairsRequested)
|
||||
self.assertEqual(self.netutils._OFFLOAD_ENABLED,
|
||||
mock_hw_offload_sd.VMQOffloadWeight)
|
||||
self.assertEqual(offloaded_sa,
|
||||
mock_hw_offload_sd.IPSecOffloadLimit)
|
||||
|
||||
@mock.patch.object(networkutils.NetworkUtils,
|
||||
'_get_setting_data_from_port_alloc')
|
||||
def test_get_profile_setting_data_from_port_alloc(self, mock_get_sd):
|
||||
|
@ -217,6 +217,62 @@ class HostUtilsTestCase(test_base.OsWinBaseTestCase):
|
||||
self._conn_scimv2.MSFT_NetAdapter.assert_called_once_with(
|
||||
InterfaceDescription=mock.sentinel.nic_name)
|
||||
|
||||
@mock.patch.object(hostutils.HostUtils, '_get_nic_hw_offload_info')
|
||||
def test_get_nic_hardware_offload_info(self, mock_get_nic_offload):
|
||||
mock_vswitch_sd = mock.Mock(VirtualSystemIdentifier=mock.sentinel.vsid)
|
||||
mock_hw_offload_sd = mock.Mock(SystemName=mock.sentinel.vsid)
|
||||
|
||||
vswitch_sds_class = self._conn.Msvm_VirtualEthernetSwitchSettingData
|
||||
vswitch_sds_class.return_value = [mock_vswitch_sd]
|
||||
hw_offload_class = self._conn.Msvm_EthernetSwitchHardwareOffloadData
|
||||
hw_offload_class.return_value = [mock_hw_offload_sd]
|
||||
|
||||
hw_offload_info = self._hostutils.get_nic_hardware_offload_info()
|
||||
|
||||
self.assertEqual([mock_get_nic_offload.return_value], hw_offload_info)
|
||||
vswitch_sds_class.assert_called_once_with()
|
||||
hw_offload_class.assert_called_once_with()
|
||||
mock_get_nic_offload.assert_called_once_with(mock_vswitch_sd,
|
||||
mock_hw_offload_sd)
|
||||
|
||||
def test_get_nic_hardware_offload_info_no_nic(self):
|
||||
self._netutils.get_vswitch_external_network_name.return_value = None
|
||||
mock_vswitch_sd = mock.Mock()
|
||||
|
||||
hw_offload_info = self._hostutils._get_nic_hw_offload_info(
|
||||
mock_vswitch_sd, mock.sentinel.hw_offload_sd)
|
||||
|
||||
self.assertIsNone(hw_offload_info)
|
||||
|
||||
@mock.patch.object(hostutils.LOG, 'warning')
|
||||
def test_get_nic_hw_offload_info(self, mock_warning):
|
||||
mock_vswitch_sd = mock.Mock()
|
||||
mock_hw_offload_sd = mock.Mock(IovVfCapacity=0)
|
||||
mock_nic = mock.Mock()
|
||||
self._conn_scimv2.MSFT_NetAdapter.return_value = [mock_nic]
|
||||
|
||||
hw_offload_info = self._hostutils._get_nic_hw_offload_info(
|
||||
mock_vswitch_sd, mock_hw_offload_sd)
|
||||
|
||||
expected = {
|
||||
'vswitch_name': mock_vswitch_sd.ElementName,
|
||||
'device_id': mock_nic.PnPDeviceID,
|
||||
'total_vfs': mock_hw_offload_sd.IovVfCapacity,
|
||||
'used_vfs': mock_hw_offload_sd.IovVfUsage,
|
||||
'total_iov_queue_pairs': mock_hw_offload_sd.IovQueuePairCapacity,
|
||||
'used_iov_queue_pairs': mock_hw_offload_sd.IovQueuePairUsage,
|
||||
'total_vmqs': mock_hw_offload_sd.VmqCapacity,
|
||||
'used_vmqs': mock_hw_offload_sd.VmqUsage,
|
||||
'total_ipsecsa': mock_hw_offload_sd.IPsecSACapacity,
|
||||
'used_ipsecsa': mock_hw_offload_sd.IPsecSAUsage,
|
||||
}
|
||||
self.assertEqual(expected, hw_offload_info)
|
||||
get_ext_net_name = self._netutils.get_vswitch_external_network_name
|
||||
get_ext_net_name.assert_called_once_with(mock_vswitch_sd.ElementName)
|
||||
self.assertTrue(mock_warning.called)
|
||||
self._conn_scimv2.MSFT_NetAdapter.assert_called_once_with(
|
||||
InterfaceDescription=get_ext_net_name.return_value)
|
||||
|
||||
def _check_get_numa_nodes_missing_info(self):
|
||||
numa_node = mock.MagicMock()
|
||||
self._hostutils._conn.Msvm_NumaNode.return_value = [
|
||||
|
@ -193,6 +193,10 @@ class HostUtils(baseutils.BaseUtilsVirt):
|
||||
- 'used_vfs': the vSwitch's number of used VFs. (<= 'total_vfs')
|
||||
"""
|
||||
|
||||
# TODO(claudiub): We have added a different method that returns all
|
||||
# of the offloading capabilities available, including SR-IOV.
|
||||
# Remove this method in S.
|
||||
|
||||
vfs = []
|
||||
|
||||
# NOTE(claudiub): A vSwitch will have to be configured to enable
|
||||
@ -229,6 +233,79 @@ class HostUtils(baseutils.BaseUtilsVirt):
|
||||
|
||||
return vfs
|
||||
|
||||
def get_nic_hardware_offload_info(self):
|
||||
"""Get host's NIC hardware offload information.
|
||||
|
||||
Hyper-V offers a few different hardware offloading options for VMs and
|
||||
their vNICs, depending on the vSwitches' NICs hardware resources and
|
||||
capabilities. These resources are managed and assigned automatically by
|
||||
Hyper-V. These resources are: VFs, IOV queue pairs, VMQs, IPsec
|
||||
security association offloads.
|
||||
|
||||
:returns: a list of dictionaries, containing the following fields:
|
||||
- 'vswitch_name': the switch name.
|
||||
- 'device_id': the switch's physical NIC's PnP device ID.
|
||||
- 'total_vfs': the switch's maximum number of VFs. (>= 0)
|
||||
- 'used_vfs': the switch's number of used VFs. (<= 'total_vfs')
|
||||
- 'total_iov_queue_pairs': the switch's maximum number of IOV
|
||||
queue pairs. (>= 'total_vfs')
|
||||
- 'used_iov_queue_pairs': the switch's number of used IOV queue
|
||||
pairs (<= 'total_iov_queue_pairs')
|
||||
- 'total_vmqs': the switch's maximum number of VMQs. (>= 0)
|
||||
- 'used_vmqs': the switch's number of used VMQs. (<= 'total_vmqs')
|
||||
- 'total_ipsecsa': the maximum number of IPsec SA offloads
|
||||
supported by the switch. (>= 0)
|
||||
- 'used_ipsecsa': the switch's number of IPsec SA offloads
|
||||
currently in use. (<= 'total_ipsecsa')
|
||||
"""
|
||||
|
||||
hw_offload_data = []
|
||||
|
||||
vswitch_sds = self._conn.Msvm_VirtualEthernetSwitchSettingData()
|
||||
hw_offload_sds = self._conn.Msvm_EthernetSwitchHardwareOffloadData()
|
||||
for vswitch_sd in vswitch_sds:
|
||||
hw_offload = [
|
||||
s for s in hw_offload_sds if
|
||||
s.SystemName == vswitch_sd.VirtualSystemIdentifier][0]
|
||||
|
||||
vswitch_offload_data = self._get_nic_hw_offload_info(
|
||||
vswitch_sd, hw_offload)
|
||||
if vswitch_offload_data:
|
||||
hw_offload_data.append(vswitch_offload_data)
|
||||
|
||||
return hw_offload_data
|
||||
|
||||
def _get_nic_hw_offload_info(self, vswitch_sd, hw_offload_sd):
|
||||
nic_name = self._netutils.get_vswitch_external_network_name(
|
||||
vswitch_sd.ElementName)
|
||||
if not nic_name:
|
||||
# NOTE(claudiub): This can happen if the vSwitch is not
|
||||
# external.
|
||||
LOG.warning("VSwitch %s is not external.", vswitch_sd.ElementName)
|
||||
return
|
||||
|
||||
# check if the vSwitch is misconfigured.
|
||||
if vswitch_sd.IOVPreferred and not hw_offload_sd.IovVfCapacity:
|
||||
LOG.warning("VSwitch %s has SR-IOV enabled, but it is not "
|
||||
"supported by the NIC or by the OS.",
|
||||
vswitch_sd.ElementName)
|
||||
|
||||
nic = self._conn_scimv2.MSFT_NetAdapter(
|
||||
InterfaceDescription=nic_name)[0]
|
||||
|
||||
return {
|
||||
'vswitch_name': vswitch_sd.ElementName,
|
||||
'device_id': nic.PnPDeviceID,
|
||||
'total_vfs': hw_offload_sd.IovVfCapacity,
|
||||
'used_vfs': hw_offload_sd.IovVfUsage,
|
||||
'total_iov_queue_pairs': hw_offload_sd.IovQueuePairCapacity,
|
||||
'used_iov_queue_pairs': hw_offload_sd.IovQueuePairUsage,
|
||||
'total_vmqs': hw_offload_sd.VmqCapacity,
|
||||
'used_vmqs': hw_offload_sd.VmqUsage,
|
||||
'total_ipsecsa': hw_offload_sd.IPsecSACapacity,
|
||||
'used_ipsecsa': hw_offload_sd.IPsecSAUsage,
|
||||
}
|
||||
|
||||
def get_numa_nodes(self):
|
||||
"""Returns the host's list of NUMA nodes.
|
||||
|
||||
|
@ -73,8 +73,8 @@ class NetworkUtils(baseutils.BaseUtilsVirt):
|
||||
_VM_SUMMARY_ENABLED_STATE = 100
|
||||
_HYPERV_VM_STATE_ENABLED = 2
|
||||
|
||||
_IOV_ENABLED = 100
|
||||
_IOV_DISABLED = 0
|
||||
_OFFLOAD_ENABLED = 100
|
||||
_OFFLOAD_DISABLED = 0
|
||||
|
||||
_ACL_DIR_IN = 1
|
||||
_ACL_DIR_OUT = 2
|
||||
@ -580,20 +580,72 @@ class NetworkUtils(baseutils.BaseUtilsVirt):
|
||||
enabled or disabled.
|
||||
:param enabled: boolean, if SR-IOV should be turned on or off.
|
||||
"""
|
||||
# TODO(claudiub): We have added a different method that sets all sorts
|
||||
# of offloading options on a vswitch port, including SR-IOV.
|
||||
# Remove this method in S.
|
||||
self.set_vswitch_port_offload(switch_port_name, sriov_enabled=enabled)
|
||||
|
||||
def set_vswitch_port_offload(self, switch_port_name, sriov_enabled=None,
|
||||
iov_queues_requested=None, vmq_enabled=None,
|
||||
offloaded_sa=None):
|
||||
"""Enables / Disables different offload options for the given port.
|
||||
|
||||
Optional prameters are ignored if they are None.
|
||||
|
||||
:param switch_port_name: the name of the port which will have VMQ
|
||||
enabled or disabled.
|
||||
:param sriov_enabled: if SR-IOV should be turned on or off.
|
||||
:param iov_queues_requested: the number of IOV queues to use. (> 1)
|
||||
:param vmq_enabled: if VMQ should be turned on or off.
|
||||
:param offloaded_sa: the number of IPsec SA offloads to use. (> 1)
|
||||
:raises os_win.exceptions.InvalidParameterValue: if an invalid value
|
||||
is passed for the iov_queues_requested or offloaded_sa parameters.
|
||||
"""
|
||||
|
||||
if iov_queues_requested is not None and iov_queues_requested < 1:
|
||||
raise exceptions.InvalidParameterValue(
|
||||
param_name='iov_queues_requested',
|
||||
param_value=iov_queues_requested)
|
||||
|
||||
if offloaded_sa is not None and offloaded_sa < 1:
|
||||
raise exceptions.InvalidParameterValue(
|
||||
param_name='offloaded_sa',
|
||||
param_value=offloaded_sa)
|
||||
|
||||
port_alloc = self._get_switch_port_allocation(switch_port_name)[0]
|
||||
|
||||
# NOTE(claudiub): All ports have a HW offload SD.
|
||||
hw_offload_sd = self._get_hw_offload_sd_from_port_alloc(port_alloc)
|
||||
desired_state = self._IOV_ENABLED if enabled else self._IOV_DISABLED
|
||||
if hw_offload_sd.IOVOffloadWeight == desired_state:
|
||||
# already in the desired state. noop.
|
||||
return
|
||||
sd_changed = False
|
||||
|
||||
hw_offload_sd.IOVOffloadWeight = desired_state
|
||||
if sriov_enabled is not None:
|
||||
desired_state = (self._OFFLOAD_ENABLED if sriov_enabled else
|
||||
self._OFFLOAD_DISABLED)
|
||||
if hw_offload_sd.IOVOffloadWeight != desired_state:
|
||||
hw_offload_sd.IOVOffloadWeight = desired_state
|
||||
sd_changed = True
|
||||
|
||||
if iov_queues_requested is not None:
|
||||
if hw_offload_sd.IOVQueuePairsRequested != iov_queues_requested:
|
||||
hw_offload_sd.IOVQueuePairsRequested = iov_queues_requested
|
||||
sd_changed = True
|
||||
|
||||
if vmq_enabled is not None:
|
||||
desired_state = (self._OFFLOAD_ENABLED if vmq_enabled else
|
||||
self._OFFLOAD_DISABLED)
|
||||
if hw_offload_sd.VMQOffloadWeight != desired_state:
|
||||
hw_offload_sd.VMQOffloadWeight = desired_state
|
||||
sd_changed = True
|
||||
|
||||
if offloaded_sa is not None:
|
||||
if hw_offload_sd.IPSecOffloadLimit != offloaded_sa:
|
||||
hw_offload_sd.IPSecOffloadLimit = offloaded_sa
|
||||
sd_changed = True
|
||||
|
||||
# NOTE(claudiub): The HW offload SD can simply be modified. No need to
|
||||
# remove it and create a new one.
|
||||
self._jobutils.modify_virt_feature(hw_offload_sd)
|
||||
if sd_changed:
|
||||
self._jobutils.modify_virt_feature(hw_offload_sd)
|
||||
|
||||
def _get_profile_setting_data_from_port_alloc(self, port_alloc):
|
||||
return self._get_setting_data_from_port_alloc(
|
||||
|
@ -0,0 +1,10 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Hyper-V can take advantage of different NIC hardware offload capabilities
|
||||
and can assign different offloading resources to VM NICs (SR-IOV, IOV
|
||||
queues, VMQs, IPSec).
|
||||
hostutils can now retrieve all the different NIC hardware offloading
|
||||
resources available to Hyper-V (depending on the physical NICs assigned).
|
||||
networkutils can now enable / assign different NIC hardware offloading
|
||||
resources to a given vSwitch port.
|
Loading…
Reference in New Issue
Block a user