MoonGen
 All Files Functions Variables Pages
memory.lua
Go to the documentation of this file.
1 ---------------------------------
2 --- @file memory.lua
3 --- @brief Memory ...
4 --- @todo TODO docu
5 ---------------------------------
6 
7 -- vim:ts=4:sw=4:noexpandtab
8 local mod = {}
9 
10 local ffi = require "ffi"
11 local dpdkc = require "dpdkc"
12 local dpdk = require "dpdk"
13 local ns = require "namespaces"
14 local serpent = require "Serpent"
15 
16 ffi.cdef [[
17  void* malloc(size_t size);
18  void free(void* buf);
19  void* alloc_huge(size_t size);
20 ]]
21 
22 local C = ffi.C
23 local cast = ffi.cast
24 
25 --- Off-heap allocation, not garbage-collected.
26 --- @param ctype a ffi type, must be a pointer or array type
27 --- @param size the amount of memory to allocate
28 function mod.alloc(ctype, size)
29  return cast(ctype, C.malloc(size))
30 end
31 
32 --- Free off-heap allocated object.
33 function mod.free(buf)
34  C.free(buf)
35 end
36 
37 --- Off-heap allocation on huge pages, not garbage-collected.
38 --- See memory.alloc.
39 --- TODO: add a free function for this
40 function mod.allocHuge(ctype, size)
41  return cast(ctype, C.alloc_huge(size))
42 end
43 
44 local mempools = {}
45 local mempoolCache = ns:get()
46 
47 local cacheEnabled = false
48 
49 --- Enable mempool recycling.
50 --- Calling this function enables the mempool cache. This prevents memory leaks
51 --- as DPDK cannot delete mempools.
52 --- Mempools with the same parameters created on the same core will be recycled.
53 --- This is not yet enabled by default because I'm not 100% confident that it works
54 --- properly in all cases.
55 --- For example, mempools passed to other tasks will probably break stuff.
56 function mod.enableCache()
57  cacheEnabled = true
58 end
59 
60 local function getPoolFromCache(socket, n, bufSize)
61  if not cacheEnabled then
62  return
63  end
64  local pool
65  mempoolCache.lock(function()
66  -- TODO: pass an iterator context to the callback
67  -- the context could then run functions like abort() or removeCurrent()
68  local result
69  mempoolCache:forEach(function(key, pool)
70  if result then
71  return
72  end
73  if pool.socket == socket
74  and pool.n == n
75  and pool.bufSize == bufSize
76  and pool.core == dpdk.getCore() then
77  result = key
78  end
79  end)
80  if result then
81  pool = mempoolCache[result].pool
82  mempoolCache[result] = nil
83  end
84  end)
85  if pool then
86  local bufs = {}
87  for i = 1, n do
88  local buf = pool:alloc(bufSize)
89  ffi.fill(buf.data, buf.len, 0)
90  bufs[#bufs + 1] = buf
91  end
92  for _, v in ipairs(bufs) do
93  dpdkc.rte_pktmbuf_free_export(v)
94  end
95  end
96  return pool
97 end
98 
99 --- Create a new memory pool.
100 --- Memory pools are recycled once the owning task terminates.
101 --- Call :retain() for mempools that are passed to other tasks.
102 --- A table with named arguments should be used.
103 --- @param args A table containing the following named arguments
104 --- @param n optional (default = 2047), size of the mempool
105 --- @param func optional, init func, called for each argument
106 --- @param socket optional (default = socket of the calling thread), NUMA association. This cannot be the only argument in the call.
107 --- @param bufSize optional the size of each buffer, can only be used if all other args are passed as well
108 function mod.createMemPool(...)
109  local args = {...}
110  if type(args[1]) == "table" then
111  args = args[1]
112  else
113  --print "[WARNING] You are using a depreciated method for calling createMemPool(...). createMemPool(...) should be used with named arguments."
114  if type(args[1]) == "function" then
115  -- (func[, socket])
116  args.socket = args[2]
117  args.func = args[1]
118  elseif type(args[2]) == "number" then
119  -- (n[, socket])
120  args.socket = args[2]
121  end
122  end
123  -- DPDK recommends to use a value of n=2^k - 1 here
124  args.n = args.n or 2047
125  args.socket = args.socket or select(2, dpdk.getCore())
126  args.bufSize = args.bufSize or 2048
127  -- TODO: get cached mempool from the mempool pool if possible and use that instead
128  -- FIXME: the todo seems to be already implemented here.
129  local mem = getPoolFromCache(args.socket, args.n, args.bufSize) or dpdkc.init_mem(args.n, args.socket, args.bufSize)
130  if args.func then
131  local bufs = {}
132  for i = 1, args.n do
133  -- TODO: make this dependent on bufSize
134  local buf = mem:alloc(1522)
135  args.func(buf)
136  bufs[#bufs + 1] = buf
137  end
138  for i, v in ipairs(bufs) do
139  dpdkc.rte_pktmbuf_free_export(v)
140  end
141  end
142  mempools[#mempools + 1] = {
143  pool = mem,
144  socket = args.socket,
145  n = args.n,
146  bufSize = args.bufSize,
147  core = dpdk.getCore()
148  }
149  return mem
150 end
151 
152 --- Free all memory pools owned by this task.
153 --- All queues using these pools must be stopped before calling this.
154 function mod.freeMemPools()
155  if not cacheEnabled then
156  return
157  end
158  for _, mem in ipairs(mempools) do
159  mempoolCache[tostring(mem.pool)] = mem
160  end
161  mempools = {}
162 end
163 
164 local mempool = {}
165 mempool.__index = mempool
166 
167 --- Retain a memory pool.
168 --- This will prevent the pool from being returned to a pool of pools once the task ends.
169 function mempool:retain()
170  for i, v in ipairs(mempools) do
171  if v.pool == self then
172  table.remove(mempools, i)
173  return
174  end
175  end
176 end
177 
178 function mempool:alloc(l)
179  local r = dpdkc.alloc_mbuf(self)
180  r.pkt.pkt_len = l
181  r.pkt.data_len = l
182  return r
183 end
184 
185 local bufArray = {}
186 
187 --- Create a new array of memory buffers (initialized to nil).
188 function mempool:bufArray(n)
189  n = n or 63
190  return setmetatable({
191  size = n,
192  array = ffi.new("struct rte_mbuf*[?]", n),
193  mem = self,
194  }, bufArray)
195 end
196 
197 do
198  local function alloc()
199  error("buf array not associated with a memory pool", 2)
200  end
201 
202  --- Create a new array of memory buffers (initialized to nil).
203  -- This buf array is not associated with a memory pool.
204  function mod.createBufArray(n)
205  -- allow self-calls
206  if self == mod then
207  n = self
208  end
209  n = n or 63
210  return setmetatable({
211  size = n,
212  array = ffi.new("struct rte_mbuf*[?]", n),
213  alloc = alloc
214  }, bufArray)
215  end
216 
217  mod.bufArray = mod.createBufArray
218 end
219 
220 function bufArray:offloadUdpChecksums(ipv4, l2Len, l3Len)
221  ipv4 = ipv4 == nil or ipv4
222  l2_len = l2_len or 14
223  if ipv4 then
224  l3_len = l3_len or 20
225  for i = 0, self.size - 1 do
226  self.array[i].ol_flags = bit.bor(self.array[i].ol_flags, dpdk.PKT_TX_IPV4_CSUM, dpdk.PKT_TX_UDP_CKSUM)
227  self.array[i].pkt.header_lengths = l2_len * 512 + l3_len
228  end
229  dpdkc.calc_ipv4_pseudo_header_checksums(self.array, self.size, 20)
230  else
231  l3_len = l3_len or 40
232  for i = 0, self.size - 1 do
233  self.array[i].ol_flags = bit.bor(self.array[i].ol_flags, dpdk.PKT_TX_UDP_CKSUM)
234  self.array[i].pkt.header_lengths = l2_len * 512 + l3_len
235  end
236  dpdkc.calc_ipv6_pseudo_header_checksums(self.array, self.size, 30)
237  end
238 end
239 
240 function bufArray:offloadIPSec(idx, mode, sec_type)
241  for i = 0, self.size - 1 do
242  local buf = self.array[i]
243  buf:offloadIPSec(idx, mode, sec_type)
244  end
245 end
246 
247 --- If called, IP chksum offloading will be done for the first n packets
248 -- in the bufArray.
249 -- @param ipv4 optional (default = true) specifies, if the buffers contain ipv4 packets
250 -- @param l2Len optional (default = 14)
251 -- @param l3Len optional (default = 20)
252 -- @param n optional (default = bufArray.size) for how many packets in the array, the operation
253 -- should be applied
254 function bufArray:offloadIPChecksums(ipv4, l2Len, l3Len, n)
255  -- please do not touch this function without carefully measuring the performance impact
256  -- FIXME: touched this.
257  -- added parameter n
258  ipv4 = ipv4 == nil or ipv4
259  n = n or self.size
260  if ipv4 then
261  l2_len = l2_len or 14
262  l3_len = l3_len or 20
263  for i = 0, n - 1 do
264  local buf = self.array[i]
265  buf.ol_flags = bit.bor(buf.ol_flags, dpdk.PKT_TX_IPV4_CSUM)
266  buf.pkt.header_lengths = l2_len * 512 + l3_len
267  end
268  end
269 end
270 
271 function bufArray:offloadTcpChecksums(ipv4, l2Len, l3Len)
272  ipv4 = ipv4 == nil or ipv4
273  l2_len = l2_len or 14
274  if ipv4 then
275  l3_len = l3_len or 20
276  for i = 0, self.size - 1 do
277  self.array[i].ol_flags = bit.bor(self.array[i].ol_flags, dpdk.PKT_TX_IPV4_CSUM, dpdk.PKT_TX_TCP_CKSUM)
278  self.array[i].pkt.header_lengths = l2_len * 512 + l3_len
279  end
280  dpdkc.calc_ipv4_pseudo_header_checksums(self.array, self.size, 25)
281  else
282  l3_len = l3_len or 40
283  for i = 0, self.size - 1 do
284  self.array[i].ol_flags = bit.bor(self.array[i].ol_flags, dpdk.PKT_TX_TCP_CKSUM)
285  self.array[i].pkt.header_lengths = l2_len * 512 + l3_len
286  end
287  dpdkc.calc_ipv6_pseudo_header_checksums(self.array, self.size, 35)
288  end
289 end
290 
291 --- Offloads VLAN tags on all packets.
292 -- Equivalent to calling pkt:setVlan(vlan, pcp, cfi) on all packets.
293 function bufArray:setVlans(vlan, pcp, cfi)
294  local tci = vlan + bit.lshift(pcp or 0, 13) + bit.lshift(cfi or 0, 12)
295  for i = 0, self.size - 1 do
296  self.array[i].pkt.vlan_tci = tci
297  self.array[i].ol_flags = bit.bor(self.array[i].ol_flags, dpdk.PKT_TX_VLAN_PKT)
298  end
299 end
300 
301 --- Allocates buffers from the memory pool and fills the array
302 function bufArray:alloc(size)
303  dpdkc.alloc_mbufs(self.mem, self.array, self.size, size)
304 end
305 
306 --- Free all buffers in the array. Stops when it encounters the first one that is null.
307 function bufArray:freeAll()
308  for i = 0, self.size - 1 do
309  if self.array[i] == nil then
310  return
311  end
312  dpdkc.rte_pktmbuf_free_export(self.array[i])
313  self.array[i] = nil
314  end
315 end
316 
317 --- Free the first n buffers.
318 function bufArray:free(n)
319  for i = 0, n - 1 do
320  if self.array[i] ~= nil then
321  dpdkc.rte_pktmbuf_free_export(self.array[i])
322  end
323  end
324 end
325 
326 function bufArray.__index(self, k)
327  -- TODO: is this as fast as I hope it to be?
328  return type(k) == "number" and self.array[k - 1] or bufArray[k]
329 end
330 
331 function bufArray.__newindex(self, i, v)
332  self.array[i - 1] = v
333 end
334 
335 function bufArray.__len(self)
336  return self.size
337 end
338 
339 do
340  local function it(self, i)
341  if i >= self.size then
342  return nil
343  end
344  return i + 1, self.array[i]
345  end
346 
347  function bufArray.__ipairs(self)
348  return it, self, 0
349  end
350 end
351 
352 
353 ffi.metatype("struct mempool", mempool)
354 
355 return mod
356 
local ffi
low-level dpdk wrapper
Definition: dpdkc.lua:6
function mod free(buf)
Free off-heap allocated object.
local mod
high-level dpdk wrapper
Definition: dpdk.lua:6
function ip4Addr add(val)
Add a number to an IPv4 address in-place.