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:
Claudiu Belu 2018-01-05 17:27:59 +02:00
parent bbaa712277
commit 63ccd40ed9
5 changed files with 247 additions and 11 deletions

View File

@ -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):

View File

@ -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 = [

View File

@ -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.

View File

@ -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(

View File

@ -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.