MoonGen
 All Files Functions Variables Pages
dpdk.lua
Go to the documentation of this file.
1 ---------------------------------
2 --- @file dpdk.lua
3 --- @brief DPDK ...
4 --- @todo TODO docu
5 ---------------------------------
6 
7 --- high-level dpdk wrapper
8 local mod = {}
9 local ffi = require "ffi"
10 local dpdkc = require "dpdkc"
11 local serpent = require "Serpent"
12 
13 -- DPDK constants (lib/librte_mbuf/rte_mbuf.h)
14 -- TODO: import more constants here
15 mod.PKT_RX_VLAN_PKT = 0x0001
16 mod.PKT_RX_IEEE1588_TMST = 0x0400
17 mod.PKT_RX_IPSEC_SECP = 0x0800
18 mod.PKT_RX_SECERR_LSB = 0x1000
19 mod.PKT_RX_SECERR_MSB = 0x2000
20 
21 mod.PKT_TX_IPV4_CSUM = 0x1000
22 mod.PKT_TX_TCP_CKSUM = 0x2000
23 mod.PKT_TX_UDP_CKSUM = 0x6000
24 mod.PKT_TX_NO_CRC_CSUM = 0x0001
25 mod.PKT_TX_IPSEC = 0x0002
26 mod.PKT_TX_VLAN_PKT = 0x0800
27 mod.PKT_TX_IEEE1588_TMST = 0x8000
28 
29 local function fileExists(f)
30  local file = io.open(f, "r")
31  if file then
32  file:close()
33  end
34  return not not file
35 end
36 
37 local cores
38 
39 --- Inits DPDK. Called by MoonGen on startup.
40 function mod.init()
41  -- register drivers
42  dpdkc.register_pmd_drivers()
43  -- TODO: support arbitrary dpdk configurations by allowing configuration in the form ["cmdLine"] = "foo"
44  local cfgFileLocations = {
45  "./dpdk-conf.lua",
46  "./lua/dpdk-conf.lua",
47  "../lua/dpdk-conf.lua",
48  "/etc/moongen/dpdk-conf.lua"
49  }
50  local cfg
51  for _, f in ipairs(cfgFileLocations) do
52  if fileExists(f) then
53  cfgScript = loadfile(f)
54  setfenv(cfgScript, setmetatable({ DPDKConfig = function(arg) cfg = arg end }, { __index = _G }))
55  local ok, err = pcall(cfgScript)
56  if not ok then
57  print("could not load DPDK config: " .. err)
58  return false
59  end
60  if not cfg then
61  print("config file does not contain DPDKConfig statement")
62  return false
63  end
64  cfg.name = f
65  break
66  end
67  end
68  if not cfg then
69  print("no DPDK config found, using defaults")
70  cfg = {}
71  end
72  local coreMask
73  if not cfg.cores then
74  -- default: use all the cores
75  local cpus = io.open("/proc/cpuinfo", "r")
76  cfg.cores = {}
77  for cpu in cpus:read("*a"):gmatch("processor : (%d+)") do
78  cfg.cores[#cfg.cores + 1] = tonumber(cpu)
79  end
80  cpus:close()
81  end
82  if type(cfg.cores) == "number" then
83  coreMask = cfg.cores
84  cores = {}
85  -- TODO: support more than 32 cores but bit operations on 64 bit types are currently not supported in luajit
86  for i = 0, 31 do
87  if bit.band(coreMask, bit.lshift(1, i)) ~= 0 then
88  cores[#cores + 1] = i
89  end
90  end
91  if cfg.cores >= 2^32 then
92  print("Warning: more than 32 cores are currently not supported in bitmask format, sorry")
93  print("Use a table as a work-around")
94  return
95  end
96  elseif type(cfg.cores) == "table" then
97  cores = cfg.cores
98  coreMask = 0
99  for i, v in ipairs(cores) do
100  coreMask = coreMask + 2^v
101  end
102  end
103  local argv = { "MoonGen" }
104  if cfg.noHugeTlbFs then
105  argv[#argv + 1] = "--no-huge"
106  end
107  argv[#argv + 1] = ("-c0x%X"):format(coreMask)
108  argv[#argv + 1] = "-n" .. (cfg.memoryChannels or 4) -- todo: auto-detect
109  local argc = #argv
110  dpdkc.rte_eal_init(argc, ffi.new("const char*[?]", argc, argv))
111  return true
112 end
113 
114 ffi.cdef[[
115  void launch_lua_core(int core, uint64_t task_id, char* userscript, char* args);
116 
117  void free(void* ptr);
118  uint64_t generate_task_id();
119  void store_result(uint64_t task_id, char* result);
120  char* get_result(uint64_t task_id);
121 ]]
122 
123 local function checkCore()
124  if MOONGEN_TASK_NAME ~= "master" then
125  error("[ERROR] This function is only available on the master task.", 2)
126  end
127 end
128 
129 local task = {}
130 task.__index = task
131 
132 local tasks = {}
133 
134 function task:new(core)
135  checkCore()
136  local obj = setmetatable({
137  -- double instead of uint64_t is easier here and okay (unless you want to start more than 2^53 tasks)
138  id = tonumber(ffi.C.generate_task_id()),
139  core = core
140  }, task)
141  tasks[core] = obj
142  return obj
143 end
144 
145 --- Wait for a task and return any arguments returned by the task
146 function task:wait()
147  checkCore()
148  while true do
149  if dpdkc.rte_eal_get_lcore_state(self.core) ~= dpdkc.RUNNING then
150  -- task is finished
151  local result = dpdkc.get_result(self.id)
152  if result == nil then
153  -- thread crashed :(
154  return
155  end
156  local resultString = ffi.string(result)
157  dpdkc.free(result)
158  return unpackAll(loadstring(resultString)())
159  end
160  ffi.C.usleep(100)
161  end
162 end
163 
164 function task:isRunning()
165  checkCore()
166  if not tasks[self.core] or task[self.core].id ~= self.id then
167  -- something else or nothing is running on this core
168  return false
169  end
170  -- this task is still on this cora, but is it still running?
171  return dpdkc.rte_eal_get_lcore_state(core) == dpdkc.RUNNING
172 end
173 
174 
175 --- Launch a LuaJIT VM on a core with the given arguments.
176 --- TODO: use proper serialization and only pass strings
177 function mod.launchLuaOnCore(core, ...)
178  checkCore()
179  local args = serpent.dump({ ... })
180  local task = task:new(core)
181  local buf = ffi.new("char[?]", #args + 1)
182  ffi.copy(buf, args)
183  local userscript = ffi.new("char[?]", #mod.userScript + 1)
184  ffi.copy(userscript, mod.userScript)
185  dpdkc.launch_lua_core(core, task.id, userscript, buf)
186  return task
187 end
188 
189 --- launches the lua file on the first free core
190 function mod.launchLua(...)
191  checkCore()
192  for i = 2, #cores do -- skip master
193  local core = cores[i]
194  local status = dpdkc.rte_eal_get_lcore_state(core)
195  if status == dpdkc.FINISHED then
196  dpdkc.rte_eal_wait_lcore(core)
197  -- should be guaranteed to be in WAIT state now according to DPDK documentation
198  status = dpdkc.rte_eal_get_lcore_state(core)
199  end
200  if status == dpdkc.WAIT then -- core is in WAIT state
201  return mod.launchLuaOnCore(core, ...)
202  end
203  end
204  error("not enough cores to start this lua task")
205 end
206 
207 ffi.cdef [[
208  int usleep(unsigned int usecs);
209 ]]
210 
211 --- waits until all slave cores have finished their jobs
212 function mod.waitForSlaves()
213  while true do
214  local allCoresFinished = true
215  for i = 2, #cores do -- skip master
216  local core = cores[i]
217  if dpdkc.rte_eal_get_lcore_state(core) == dpdkc.RUNNING then
218  allCoresFinished = false
219  break
220  end
221  end
222  if allCoresFinished then
223  return
224  end
225  ffi.C.usleep(100)
226  end
227 end
228 
229 function mod.getCores()
230  return cores
231 end
232 
233 --- get the CPU's TSC
234 function mod.getCycles()
235  return dpdkc.rte_rdtsc()
236 end
237 
238 --- get the TSC frequency
239 function mod.getCyclesFrequency()
240  return tonumber(dpdkc.rte_get_tsc_hz())
241 end
242 
243 --- gets the time in seconds
244 function mod.getTime()
245  return tonumber(mod.getCycles()) / tonumber(mod.getCyclesFrequency())
246 end
247 
248 --- set total run time of the test (to be called from master core on startup, shared between all cores)
249 function mod.setRuntime(time)
250  dpdkc.set_runtime(time * 1000)
251 end
252 
253 --- Returns false once the app receives SIGTERM or SIGINT, the time set via setRuntime expires, or when a thread calls dpdk.stop().
254 -- @param extraTime additional time in milliseconds before false will be returned
255 function mod.running(extraTime)
256  return dpdkc.is_running(extraTime or 0) == 1 -- luajit-2.0.3 does not like bool return types (TRACE NYI: unsupported C function type)
257 end
258 
259 --- request all tasks to exit
260 function mod.stop()
261  dpdkc.set_runtime(0)
262 end
263 
264 --- Delay by t milliseconds. Note that this does not sleep the actual thread;
265 --- the time is spent in a busy wait loop by DPDK.
266 function mod.sleepMillis(t)
267  dpdkc.rte_delay_ms_export(t)
268 end
269 
270 --- Delay by t microseconds. Note that this does not sleep the actual thread;
271 --- the time is spent in a busy wait loop by DPDK. This means that this sleep
272 --- is somewhat more accurate than relying on the OS.
273 function mod.sleepMicros(t)
274  dpdkc.rte_delay_us_export(t)
275 end
276 
277 --- Sleep by t milliseconds by calling usleep().
278 function mod.sleepMillisIdle(t)
279  ffi.C.usleep(t * 1000)
280 end
281 
282 --- Sleep by t microseconds by calling usleep().
283 function mod.sleepMicrosIdle(t)
284  ffi.C.usleep(t)
285 end
286 
287 --- Get the core and socket id for the current thread
288 function mod.getCore()
289  return dpdkc.get_current_core(), dpdkc.get_current_socket()
290 end
291 
292 function mod.disableBadSocketWarning()
293  MOONGEN_IGNORE_BAD_NUMA_MAPPING = true
294 end
295 
296 return mod
297 
function mod waitForSlaves()
waits until all slave cores have finished their jobs
local ffi
low-level dpdk wrapper
Definition: dpdkc.lua:6
function mod free(buf)
Free off-heap allocated object.
function pkt dump(bytes)
Dumps the packet data cast to the best fitting packet struct.
function mod new(n)
function task wait()
Wait for a task and return any arguments returned by the task.
local mod
high-level dpdk wrapper
Definition: dpdk.lua:6
function mod config(...)
Configure a device.
function mod init()
Inits DPDK. Called by MoonGen on startup.
function mod launchLuaOnCore(core,...)
Launch a LuaJIT VM on a core with the given arguments.
function mod running(extraTime)
Returns false once the app receives SIGTERM or SIGINT, the time set via setRuntime expires...