Skip to content
Snippets Groups Projects
Commit 597b01ee authored by Martin Teichmann's avatar Martin Teichmann
Browse files

use mmap for ebpf arrays

parent 1118f0d3
No related branches found
No related tags found
No related merge requests found
......@@ -16,19 +16,19 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from itertools import chain
from mmap import mmap
from struct import pack_into, unpack_from, calcsize
from .ebpf import FuncId, Map, MemoryDesc
from .bpf import create_map, lookup_elem, MapType, update_elem
from .ebpf import FuncId, Map, Memory, MemoryDesc, Opcode, SubProgram
from .bpf import create_map, lookup_elem, MapType, MapFlags, update_elem
class ArrayGlobalVarDesc(MemoryDesc):
base_register = 0
def __init__(self, map, fmt, write=False):
def __init__(self, map, fmt):
self.map = map
self.fmt = fmt
self.write = write
def fmt_addr(self, ebpf):
return self.fmt, ebpf.__dict__[self.name]
......@@ -63,49 +63,16 @@ class ArrayGlobalVarDesc(MemoryDesc):
class ArrayMapAccess:
"""This is the array map proper"""
def __init__(self, fd, write_size, size):
self.fd = fd
self.write_size = write_size
def __init__(self, data, size):
self.data = data
self.size = size
self.data = bytearray(size)
def read(self):
"""read all variables in the map from EBPF to user space"""
self.data = lookup_elem(self.fd, b"\0\0\0\0", self.size)
def write(self):
"""write all variables in the map from user space to EBPF
*all* variables are written, even those not marked ``write=True``
"""
update_elem(self.fd, b"\0\0\0\0", self.data, 0)
def readwrite(self):
"""read variables from EBPF and write them out immediately
This reads all variables, swaps in the user-modified values for
the ``write=True`` variables, and writes the out again
immediately.
This means that even the read-only variables will be overwritten
with the values we have just read. If the EBPF program changed
the value in the meantime, that may be a problem.
Note that after this method returns, all *write* variables
will have the value from the EBPF program in user space, and
vice-versa.
"""
write = self.data[:self.write_size]
data = lookup_elem(self.fd, b"\0\0\0\0", self.size)
self.data[:] = data
data[:self.write_size] = write
update_elem(self.fd, b"\0\0\0\0", data, 0)
class ArrayMap(Map):
"""A descriptor for an array map"""
def globalVar(self, fmt="I", write=False):
return ArrayGlobalVarDesc(self, fmt, write)
def globalVar(self, fmt="I"):
return ArrayGlobalVarDesc(self, fmt)
def collect(self, ebpf):
collection = []
......@@ -113,32 +80,25 @@ class ArrayMap(Map):
for prog in chain([ebpf], ebpf.subprograms):
for k, v in prog.__class__.__dict__.items():
if isinstance(v, ArrayGlobalVarDesc):
collection.append((v.write, calcsize(v.fmt), prog, k))
collection.sort(key=lambda t: t[:2], reverse=True)
collection.append((calcsize(v.fmt), prog, k))
collection.sort(key=lambda t: t[0], reverse=True)
position = 0
last_write = write = True
for write, size, prog, name in collection:
if last_write != write:
position = (position + 7) & -8
write_size = position
for size, prog, name in collection:
prog.__dict__[name] = position
position += size
last_write = write
if write: # there are read variables
return position, position
else:
return write_size, position
return position
def __set_name__(self, owner, name):
self.name = name
def init(self, ebpf):
setattr(ebpf, self.name, 0)
write_size, size = self.collect(ebpf)
size = self.collect(ebpf)
if not size: # nobody is actually using the map
return
fd = create_map(MapType.ARRAY, 4, size, 1)
setattr(ebpf, self.name, ArrayMapAccess(fd, write_size, size))
fd = create_map(MapType.ARRAY, 4, size, 1, MapFlags.MMAPABLE)
data = mmap(fd, size)
setattr(ebpf, self.name, ArrayMapAccess(data, size))
with ebpf.save_registers(list(range(6))), ebpf.get_stack(4) as stack:
ebpf.mI[ebpf.r10 + stack] = 0
ebpf.r1 = ebpf.get_fd(fd)
......
......@@ -19,7 +19,7 @@
A module that wraps the `bpf` system call in Python, using `ctypes`.
"""
from ctypes import CDLL, c_int, get_errno, cast, c_void_p, create_string_buffer, c_char_p, addressof, c_char
from enum import Enum
from enum import Enum, Flag
from struct import pack, unpack
from platform import machine
......@@ -60,6 +60,10 @@ class MapType(Enum):
SOCKHASH = 18
class MapFlags(Flag):
MMAPABLE = 1 << 10
class ProgType(Enum):
UNSPEC = 0
SOCKET_FILTER = 1
......@@ -96,9 +100,12 @@ def bpf(cmd, fmt, *args):
raise OSError(get_errno(), strerror(get_errno()))
return ret, unpack(fmt, attr.raw)
def create_map(map_type, key_size, value_size, max_entries):
def create_map(map_type, key_size, value_size, max_entries,
attributes=MapFlags(0)):
assert isinstance(map_type, MapType)
return bpf(0, "IIII", map_type.value, key_size, value_size, max_entries)[0]
assert isinstance(attributes, MapFlags)
return bpf(0, "IIIII", map_type.value, key_size, value_size, max_entries,
attributes.value)[0]
def lookup_elem(fd, key, size):
value = bytearray(size)
......
......@@ -175,7 +175,8 @@ class TerminalVar:
class DeviceVar(ArrayGlobalVarDesc):
def __init__(self, size="I", write=False):
super().__init__(FastSyncGroup.properties, size, write)
super().__init__(FastSyncGroup.properties, size)
self.write = write
def __get__(self, instance, owner):
if instance is None:
......@@ -316,7 +317,6 @@ class FastEtherCat(SimpleEtherCat):
lastcounts = [0] * 64
while True:
t0 = time()
self.ebpf.variables.read()
counts = self.ebpf.counters
for i, sg in self.sync_groups.items():
if ((counts[i] ^ lastcounts[i]) & 0xffff == 0
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment