Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ethtool: add ring support to ethtool #1152

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 128 additions & 1 deletion pyroute2/ethtool/ethtool.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
)
from pyroute2.ethtool.ioctl import WAKE_NAMES, IoctlEthtool
from pyroute2.netlink.exceptions import NetlinkError
from pyroute2.netlink.generic.ethtool import NlEthtool
from pyroute2.netlink.generic.ethtool import NlEthtool, ethtool_rings_msg

INT32MINUS_UINT32 = c_uint32(-1).value
INT16MINUS_UINT16 = c_uint16(-1).value
Expand Down Expand Up @@ -257,6 +257,108 @@ def from_netlink(cls, nl_link_mode):
)


class EthtoolRings(
namedtuple(
'EthtoolRings',
(
"rx_max",
"rx_mini_max",
"rx_jumbo_max",
"tx_max",
"rx",
"rx_mini",
"rx_jumbo",
"tx",
"rx_buf_len",
"tcp_data_split",
"cqe_size",
"tx_push",
"rx_push",
"tx_push_buf_len",
"tx_push_buf_len_max",
),
)
):
nl_attributs_dict = {
"rx_max": 'ETHTOOL_A_RINGS_RX_MAX',
"rx_mini_max": 'ETHTOOL_A_RINGS_RX_MINI_MAX',
"rx_jumbo_max": 'ETHTOOL_A_RINGS_RX_JUMBO_MAX',
"tx_max": 'ETHTOOL_A_RINGS_TX_MAX',
"rx": 'ETHTOOL_A_RINGS_RX',
"rx_mini": 'ETHTOOL_A_RINGS_RX_MINI',
"rx_jumbo": 'ETHTOOL_A_RINGS_RX_JUMBO',
"tx": 'ETHTOOL_A_RINGS_TX',
"rx_buf_len": 'ETHTOOL_A_RINGS_RX_BUF_LEN',
"tcp_data_split": 'ETHTOOL_A_RINGS_TCP_DATA_SPLIT',
"cqe_size": 'ETHTOOL_A_RINGS_CQE_SIZE',
"tx_push": 'ETHTOOL_A_RINGS_TX_PUSH',
"rx_push": 'ETHTOOL_A_RINGS_RX_PUSH',
"tx_push_buf_len": 'ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN',
"tx_push_buf_len_max": 'ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX',
}

def __new__(
cls,
rx_max=None,
rx_mini_max=None,
rx_jumbo_max=None,
tx_max=None,
rx=None,
rx_mini=None,
rx_jumbo=None,
tx=None,
rx_buf_len=None,
tcp_data_split=None,
cqe_size=None,
tx_push=None,
rx_push=None,
tx_push_buf_len=None,
tx_push_buf_len_max=None,
):
return super(EthtoolRings, cls).__new__(
cls,
rx_max,
rx_mini_max,
rx_jumbo_max,
tx_max,
rx,
rx_mini,
rx_jumbo,
tx,
rx_buf_len,
tcp_data_split,
cqe_size,
tx_push,
rx_push,
tx_push_buf_len,
tx_push_buf_len_max,
)

@classmethod
def from_netlink(cls, nl_rings):
nl_rings = nl_rings[0]
return cls(
**{
cls_attr: nl_rings.get_attr(netlink_attr)
for cls_attr, netlink_attr in cls.nl_attributs_dict.items()
}
)

def to_netlink(self):
nl_rings_attrs = ethtool_rings_msg()
for cls_attr, netlink_attr in self.nl_attributs_dict.items():
attr = getattr(self, cls_attr)
if attr is not None:
nl_rings_attrs["attrs"].append((netlink_attr, attr))
return nl_rings_attrs

@classmethod
def from_ioctl(cls, ioctl_rings):
ioctl_rings = dict(ioctl_rings)
ioctl_rings.pop("cmd")
return cls(**ioctl_rings)


class Ethtool:
def __init__(self):
self._with_ioctl = IoctlEthtool()
Expand Down Expand Up @@ -330,6 +432,31 @@ def get_wol(self, ifname):
wol_mode = self._with_ioctl.get_wol()
return EthtoolWakeOnLan.from_ioctl(wol_mode)

def get_rings(self, ifname, with_netlink=None):
try:
rings = self._nl_exec(
self._with_nl.get_rings, with_netlink, ifname
)
rings = EthtoolRings.from_netlink(rings)
except UseIoctl:
self._with_ioctl.change_ifname(ifname)
rings_info = self._with_ioctl.get_rings()
rings = EthtoolRings.from_ioctl(rings_info)
return rings

def set_rings(self, ifname, with_netlink=None, **kwargs):
try:
rings = EthtoolRings(**kwargs).to_netlink()
self._nl_exec(self._with_nl.set_rings, with_netlink, rings, ifname)
except UseIoctl:
self._with_ioctl.change_ifname(ifname)
ioctl_rings = self._with_ioctl.get_rings()
for name, value in kwargs.items():
if name in ioctl_rings.keys() and ioctl_rings[name] != value:
ioctl_rings[name] = value

self._with_ioctl.set_rings(ioctl_rings)

def get_features(self, ifname):
self._with_ioctl.change_ifname(ifname)
return EthtoolFeatures.from_ioctl(self._with_ioctl.get_features())
Expand Down
30 changes: 30 additions & 0 deletions pyroute2/ethtool/ioctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
ETHTOOL_GSTATS = 0x0000001D
ETH_GSTRING_LEN = 32

ETHTOOL_GRINGPARAM = 0x00000010
ETHTOOL_SRINGPARAM = 0x00000011

ETHTOOL_GRXCSUM = 0x00000014
ETHTOOL_SRXCSUM = 0x00000015
ETHTOOL_GTXCSUM = 0x00000016
Expand Down Expand Up @@ -340,6 +343,21 @@ class FeatureState(ctypes.Structure):
_fields_ = [("off_flags", ctypes.c_uint32), ("features", EthtoolGfeatures)]


class EthtoolRingParam(DictStruct):
_pack_ = 1
_fields_ = [
("cmd", ctypes.c_uint32),
("rx_max", ctypes.c_uint32),
("rx_mini_max", ctypes.c_uint32),
("rx_jumbo_max", ctypes.c_uint32),
("tx_max", ctypes.c_uint32),
("rx", ctypes.c_uint32),
("rx_mini", ctypes.c_uint32),
("rx_jumbo", ctypes.c_uint32),
("tx", ctypes.c_uint32),
]


class IfReqData(ctypes.Union):
dummy = generate_EthtoolGstrings(0)
_fields_ = [
Expand All @@ -353,6 +371,7 @@ class IfReqData(ctypes.Union):
("sfeatures", ctypes.POINTER(EthtoolSfeatures)),
("glinksettings", ctypes.POINTER(IoctlEthtoolLinkSettings)),
("wolinfo", ctypes.POINTER(EthtoolWolInfo)),
("rings", ctypes.POINTER(EthtoolRingParam)),
]


Expand Down Expand Up @@ -627,3 +646,14 @@ def get_wol(self):
self.ifreq.wolinfo = ctypes.pointer(cmd)
self.ioctl()
return cmd

def get_rings(self):
cmd = EthtoolRingParam(cmd=ETHTOOL_GRINGPARAM)
self.ifreq.rings = ctypes.pointer(cmd)
self.ioctl()
return cmd

def set_rings(self, rings):
rings.cmd = ETHTOOL_SRINGPARAM
self.ifreq.rings = ctypes.pointer(rings)
self.ioctl()
59 changes: 58 additions & 1 deletion pyroute2/netlink/generic/ethtool.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
from pyroute2.netlink import NLA_F_NESTED, NLM_F_REQUEST, genlmsg, nla
from pyroute2.netlink import (
NLA_F_NESTED,
NLM_F_ACK,
NLM_F_REQUEST,
genlmsg,
nla,
)
from pyroute2.netlink.exceptions import NetlinkError
from pyroute2.netlink.generic import GenericNetlinkSocket

Expand All @@ -16,6 +22,12 @@
ETHTOOL_MSG_DEBUG_SET = 8
ETHTOOL_MSG_WOL_GET = 9
ETHTOOL_MSG_WOL_SET = 10
ETHTOOL_MSG_FEATURES_GET = 11
ETHTOOL_MSG_FEATURES_SET = 12
ETHTOOL_MSG_PRIVFLAGS_GET = 13
ETHTOOL_MSG_PRIVFLAGS_SET = 14
ETHTOOL_MSG_RINGS_GET = 15
ETHTOOL_MSG_RINGS_SET = 16


class ethtoolheader(nla):
Expand Down Expand Up @@ -149,6 +161,30 @@ class ethtool_wol_msg(genlmsg):
ethtoolbitset = ethtoolbitset


class ethtool_rings_msg(genlmsg):
nla_map = (
('ETHTOOL_A_RINGS_UNSPEC', 'none'),
('ETHTOOL_A_RINGS_HEADER', 'ethtoolheader'),
('ETHTOOL_A_RINGS_RX_MAX', 'uint32'),
('ETHTOOL_A_RINGS_RX_MINI_MAX', 'uint32'),
('ETHTOOL_A_RINGS_RX_JUMBO_MAX', 'uint32'),
('ETHTOOL_A_RINGS_TX_MAX', 'uint32'),
('ETHTOOL_A_RINGS_RX', 'uint32'),
('ETHTOOL_A_RINGS_RX_MINI', 'uint32'),
('ETHTOOL_A_RINGS_RX_JUMBO', 'uint32'),
('ETHTOOL_A_RINGS_TX', 'uint32'),
('ETHTOOL_A_RINGS_RX_BUF_LEN', 'uint32'),
('ETHTOOL_A_RINGS_TCP_DATA_SPLIT', 'uint8'),
('ETHTOOL_A_RINGS_CQE_SIZE', 'uint32'),
('ETHTOOL_A_RINGS_TX_PUSH', 'uint8'),
('ETHTOOL_A_RINGS_RX_PUSH', 'uint8'),
('ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN', 'uint32'),
('ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX', 'uint32'),
)

ethtoolheader = ethtoolheader


class NlEthtool(GenericNetlinkSocket):
def _do_request(self, msg, msg_flags=NLM_F_REQUEST):
return self.nlm_request(msg, msg_type=self.prid, msg_flags=msg_flags)
Expand Down Expand Up @@ -231,3 +267,24 @@ def get_wol(self, ifname=None, ifindex=None):

self.bind(ETHTOOL_GENL_NAME, ethtool_wol_msg)
return self._do_request(msg)

def get_rings(self, ifname=None, ifindex=None):
msg = ethtool_rings_msg()
msg["cmd"] = ETHTOOL_MSG_RINGS_GET
msg["version"] = ETHTOOL_GENL_VERSION
msg["attrs"].append(
('ETHTOOL_A_RINGS_HEADER', self._get_dev_header(ifname, ifindex))
)

self.bind(ETHTOOL_GENL_NAME, ethtool_rings_msg)
return self._do_request(msg)

def set_rings(self, rings, ifname=None, ifindex=None):
rings["cmd"] = ETHTOOL_MSG_RINGS_SET
rings["version"] = ETHTOOL_GENL_VERSION
rings["attrs"].append(
('ETHTOOL_A_RINGS_HEADER', self._get_dev_header(ifname, ifindex))
)

self.bind(ETHTOOL_GENL_NAME, ethtool_rings_msg)
return self._do_request(rings, msg_flags=NLM_F_REQUEST | NLM_F_ACK)