MoonGen
 All Files Functions Variables Pages
device.lua
Go to the documentation of this file.
1 ---------------------------------
2 --- @file device.lua
3 --- @brief Device ...
4 --- @todo TODO docu
5 ---------------------------------
6 
7 local mod = {}
8 
9 local ffi = require "ffi"
10 local dpdkc = require "dpdkc"
11 local dpdk = require "dpdk"
12 local memory = require "memory"
13 local serpent = require "Serpent"
14 local errors = require "error"
15 require "headers"
16 
17 -- FIXME: fix this ugly duplicated code enum
18 mod.RSS_FUNCTION_IPV4 = 1
19 mod.RSS_FUNCTION_IPV4_TCP = 2
20 mod.RSS_FUNCTION_IPV4_UDP = 3
21 mod.RSS_FUNCTION_IPV6 = 4
22 mod.RSS_FUNCTION_IPV6_TCP = 5
23 mod.RSS_FUNCTION_IPV6_UDP = 6
24 
25 ffi.cdef[[
26  void rte_eth_macaddr_get ( uint8_t port_id,
27  struct ether_addr * mac_addr
28  )
29 ]]
30 
31 mod.PCI_ID_X540 = 0x80861528
32 mod.PCI_ID_X520 = 0x8086154D
33 mod.PCI_ID_82599 = 0x808610FB
34 mod.PCI_ID_82580 = 0x8086150E
35 mod.PCI_ID_82576 = 0x80861526
36 mod.PCI_ID_XL710 = 0x80861583
37 
38 function mod.init()
39  dpdkc.rte_pmd_init_all_export();
40  dpdkc.rte_eal_pci_probe();
41 end
42 
43 function mod.numDevices()
44  return dpdkc.rte_eth_dev_count();
45 end
46 
47 local dev = {}
48 dev.__index = dev
49 dev.__type = "device"
50 
51 function dev:__tostring()
52  return ("[Device: id=%d]"):format(self.id)
53 end
54 
55 function dev:__serialize()
56  return ('local dev = require "device" return dev.get(%d)'):format(self.id), true
57 end
58 
59 local txQueue = {}
60 txQueue.__index = txQueue
61 txQueue.__type = "txQueue"
62 
63 function txQueue:__tostring()
64  return ("[TxQueue: id=%d, qid=%d]"):format(self.id, self.qid)
65 end
66 
67 function txQueue:__serialize()
68  return ('local dev = require "device" return dev.get(%d):getTxQueue(%d)'):format(self.id, self.qid), true
69 end
70 
71 local rxQueue = {}
72 rxQueue.__index = rxQueue
73 rxQueue.__type = "rxQueue"
74 
75 function rxQueue:__tostring()
76  return ("[RxQueue: id=%d, qid=%d]"):format(self.id, self.qid)
77 end
78 
79 function rxQueue:__serialize()
80  return ('local dev = require "device" return dev.get(%d):getRxQueue(%d)'):format(self.id, self.qid), true
81 end
82 
83 local devices = {}
84 
85 --- Configure a device
86 --- @param args A table containing the following named arguments
87 --- port Port to configure
88 --- mempool optional (default = create a new mempool) Mempool to associate to the device
89 --- rxQueues optional (default = 1) Number of RX queues to configure
90 --- txQueues optional (default = 1) Number of TX queues to configure
91 --- rxDescs optional (default = 512)
92 --- txDescs optional (default = 256)
93 --- speed optional (default = 0)
94 --- dropEnable optional (default = true)
95 --- rssNQueues optional (default = 0) If this is >0 RSS will be activated for
96 --- this device. Incomming packates will be distributed to the
97 --- rxQueues number 0 to (rssNQueues - 1). For a fair distribution use one of
98 --- the following values (1, 2, 4, 8, 16). Values greater than 16 are not
99 --- allowed.
100 --- rssFunctions optional (default = all supported functions) A Table,
101 --- containing hashing methods, which can be used for RSS.
102 --- Possible methods are:
103 --- dev.RSS_FUNCTION_IPV4
104 --- dev.RSS_FUNCTION_IPV4_TCP
105 --- dev.RSS_FUNCTION_IPV4_UDP
106 --- dev.RSS_FUNCTION_IPV6
107 --- dev.RSS_FUNCTION_IPV6_TCP
108 --- dev.RSS_FUNCTION_IPV6_UDP
109 --- @todo FIXME: add description for speed and dropEnable parameters.
110 function mod.config(...)
111  local args = {...}
112  if #args > 1 or type((...)) == "number" then
113  -- this is for legacy compatibility when calling the function without named arguments
114  print("[WARNING] You are using a deprecated method for invoking device.config. config(...) should be used with named arguments. For details: see documentation")
115  if not args[2] or type(args[2]) == "number" then
116  args.port = args[1]
117  args.rxQueues = args[2]
118  args.txQueues = args[3]
119  args.rxDescs = args[4]
120  args.txDescs = args[5]
121  args.speed = args[6]
122  args.dropEnable = args[7]
123  else
124  args.port = args[1]
125  args.mempool = args[2]
126  args.rxQueues = args[3]
127  args.txQueues = args[4]
128  args.rxDescs = args[5]
129  args.txDescs = args[6]
130  args.speed = args[7]
131  args.dropEnable = args[8]
132  end
133  elseif #args == 1 then
134  -- here we receive named arguments
135  args = args[1]
136  else
137  errorf("Device config needs at least one argument.")
138  end
139 
140  args.rxQueues = args.rxQueues or 1
141  args.txQueues = args.txQueues or 1
142  args.rxDescs = args.rxDescs or 512
143  args.txDescs = args.txDescs or 256
144  args.rssNQueues = args.rssNQueues or 0
145  args.rssFunctions = args.rssFunctions or {mod.RSS_FUNCTION_IPV4, mod.RSS_FUNCTION_IPV4_UDP, mod.RSS_FUNCTION_IPV4_TCP, mod.RSS_FUNCTION_IPV6, mod.RSS_FUNCTION_IPV6_UDP, mod.RSS_FUNCTION_IPV6_TCP}
146  -- create a mempool with enough memory to hold tx, as well as rx descriptors
147  -- FIXME: should n = 2^k-1 here too?
148  args.mempool = args.mempool or memory.createMemPool{n = args.rxQueues * args.rxDescs + args. txQueues * args.txDescs, socket = dpdkc.get_socket(args.port)}
149  if devices[args.port] and devices[args.port].initialized then
150  printf("[WARNING] Device %d already configured, skipping initilization", args.port)
151  return mod.get(args.port)
152  end
153  args.speed = args.speed or 0
154  args.dropEnable = args.dropEnable == nil and true
155  if args.rxQueues == 0 or args.txQueues == 0 then
156  -- dpdk does not like devices without rx/tx queues :(
157  errorf("cannot initialize device without %s queues", args.rxQueues == 0 and args.txQueues == 0 and "rx and tx" or args.rxQueues == 0 and "rx" or "tx")
158  end
159  -- configure rss stuff
160  local rss_enabled = 0
161  local rss_hash_mask = ffi.new("struct mg_rss_hash_mask")
162  if(args.rssNQueues > 0) then
163  for i, v in ipairs(args.rssFunctions) do
164  if (v == mod.RSS_FUNCTION_IPV4) then
165  rss_hash_mask.ipv4 = 1
166  end
167  if (v == mod.RSS_FUNCTION_IPV4_TCP) then
168  rss_hash_mask.tcp_ipv4 = 1
169  end
170  if (v == mod.RSS_FUNCTION_IPV4_UDP) then
171  rss_hash_mask.udp_ipv4 = 1
172  end
173  if (v == mod.RSS_FUNCTION_IPV6) then
174  rss_hash_mask.ipv6 = 1
175  end
176  if (v == mod.RSS_FUNCTION_IPV6_TCP) then
177  rss_hash_mask.tcp_ipv6 = 1
178  end
179  if (v == mod.RSS_FUNCTION_IPV6_UDP) then
180  rss_hash_mask.udp_ipv6 = 1
181  end
182  end
183  rss_enabled = 1
184  end
185  -- TODO: support options
186  local rc = dpdkc.configure_device(args.port, args.rxQueues, args.txQueues, args.rxDescs, args.txDescs, args.speed, args.mempool, args.dropEnable, rss_enabled, rss_hash_mask)
187  if rc ~= 0 then
188  errorf("could not configure device %d: error %d", args.port, rc)
189  end
190  local dev = mod.get(args.port)
191  dev.initialized = true
192  if rss_enabled == 1 then
193  dev:setRssNQueues(args.rssNQueues)
194  end
195  return dev
196 end
197 
198 ffi.cdef[[
199 /**
200  * A structure used to configure Redirection Table of the Receive Side
201  * Scaling (RSS) feature of an Ethernet port.
202  */
203 struct rte_eth_rss_reta {
204  /** First 64 mask bits indicate which entry(s) need to updated/queried. */
205  uint64_t mask_lo;
206  /** Second 64 mask bits indicate which entry(s) need to updated/queried. */
207  uint64_t mask_hi;
208  uint8_t reta[128]; /**< 128 RETA entries*/
209 };
210 
211 int mg_rte_eth_dev_rss_reta_update ( uint8_t port,
212  struct rte_eth_rss_reta * reta_conf
213  );
214 int rte_eth_dev_rss_reta_update ( uint8_t port,
215  struct rte_eth_rss_reta * reta_conf
216  );
217 ]]
218 
219 function dev:setRssNQueues(n)
220  if(n>16)then
221  errorf("Maximum possible numbers of RSS queues is 16")
222  return
223  end
224  if(({[1]=1, [2]=1, [4]=1, [8]=1, [16]=1})[n] == nil) then
225  printf("[WARNING] RSS distribution to queues will not be fair. Fair distribution is only achieved with a number of Queues equal to 1, 2, 4, 8 or 16. However you are currently using %d queues", n)
226  end
227  local reta = ffi.new("struct rte_eth_rss_reta")
228 
229  local npq = 128/n
230  local queue = 0
231  for i=0,127 do
232  reta.reta[i] = queue
233  if (queue < n - 1) then
234  queue = queue+1
235  else
236  queue = 0
237  end
238  end
239 
240  -- the mg_ version of rte_eth_dev_rss_reta_update() will also write the mask
241  -- to the reta_config struct, as lua can not do 64bit unsigned int operations.
242  local ret = ffi.C.mg_rte_eth_dev_rss_reta_update(self.id, reta)
243  if (ret ~= 0) then
244  errorf("ERROR setting up RETA table: " .. errors.getstr(-ret))
245  end
246 end
247 
248 
249 
250 function mod.get(id)
251  if devices[id] then
252  return devices[id]
253  end
254  devices[id] = setmetatable({ id = id, rxQueues = {}, txQueues = {} }, dev)
255  if MOONGEN_TASK_NAME ~= "master" and not MOONGEN_IGNORE_BAD_NUMA_MAPPING then
256  -- check the NUMA association if we are running in a worker thread
257  -- (it's okay to do the initial config from the wrong socket, but sending packets from it is a bad idea)
258  local devSocket = devices[id]:getSocket()
259  local core, threadSocket = dpdk.getCore()
260  if devSocket ~= threadSocket then
261  printf("[WARNING] You are trying to use %s (attached to the CPU socket %d) from a thread on core %d on socket %d!",
262  devices[id], devSocket, core, threadSocket)
263  printf("[WARNING] This can significantly impact the performance or even not work at all")
264  printf("[WARNING] You can change the used CPU cores in dpdk-conf.lua or by using dpdk.launchLuaOnCore(core, ...)")
265  end
266  end
267  return devices[id]
268 end
269 
270 function dev:getTxQueue(id)
271  local tbl = self.txQueues
272  if tbl[id] then
273  return tbl[id]
274  end
275  tbl[id] = setmetatable({ id = self.id, qid = id, dev = self }, txQueue)
276  tbl[id]:getTxRate()
277  return tbl[id]
278 end
279 
280 function dev:getRxQueue(id)
281  local tbl = self.rxQueues
282  if tbl[id] then
283  return tbl[id]
284  end
285  tbl[id] = setmetatable({ id = self.id, qid = id, dev = self }, rxQueue)
286  return tbl[id]
287 end
288 
289 
290 --- Waits until all given devices are initialized by calling wait() on them.
291 function mod.waitForLinks(...)
292  local ports
293  if select("#", ...) == 0 then
294  ports = {}
295  for port, dev in pairs(devices) do
296  if dev.initialized then
297  ports[#ports + 1] = port
298  end
299  end
300  else
301  ports = { ... }
302  end
303  print("Waiting for devices to come up...")
304  local portsUp = 0
305  local portsSeen = {} -- do not wait twice if a port occurs more than once (e.g. if rx == tx)
306  for i, port in ipairs(ports) do
307  local port = mod.get(port)
308  if not portsSeen[port] then
309  portsSeen[port] = true
310  portsUp = portsUp + (port:wait() and 1 or 0)
311  end
312  end
313  printf("%d devices are up.", portsUp)
314 end
315 
316 
317 --- Wait until the device is fully initialized and up to maxWait seconds to establish a link.
318 -- @param maxWait maximum number of seconds to wait for the link, default = 9
319 -- This function then reports the current link state on stdout
320 function dev:wait(maxWait)
321  maxWait = maxWait or 9
322  local link
323  repeat
324  link = self:getLinkStatus()
325  if maxWait > 0 then
326  dpdk.sleepMillisIdle(1000)
327  maxWait = maxWait - 1
328  else
329  break
330  end
331  until link.status
332  self.speed = link.speed
333  printf("Device %d (%s) is %s: %s%s MBit/s", self.id, self:getMacString(), link.status and "up" or "DOWN", link.duplexAutoneg and "" or link.duplex and "full-duplex " or "half-duplex ", link.speed)
334  return link.status
335 end
336 
337 
338 function dev:getLinkStatus()
339  local link = ffi.new("struct rte_eth_link")
340  dpdkc.rte_eth_link_get_nowait(self.id, link)
341  return { status = link.link_status == 1, duplexAutoneg = link.link_duplex == 0, duplex = link.link_duplex == 2, speed = link.link_speed }
342 end
343 
344 function dev:getMacString()
345  local buf = ffi.new("char[20]")
346  dpdkc.get_mac_addr(self.id, buf)
347  return ffi.string(buf)
348 end
349 
350 function dev:getMac()
351  -- TODO: optimize
352  return parseMacAddress(self:getMacString())
353 end
354 
355 function dev:getPciId()
356  return dpdkc.get_pci_id(self.id)
357 end
358 
359 function dev:getSocket()
360  return dpdkc.get_socket(self.id)
361 end
362 
363 local deviceNames = {
364  [mod.PCI_ID_82576] = "82576 Gigabit Network Connection",
365  [mod.PCI_ID_82580] = "82580 Gigabit Network Connection",
366  [mod.PCI_ID_82599] = "82599EB 10-Gigabit SFI/SFP+ Network Connection",
367  [mod.PCI_ID_X520] = "Ethernet 10G 2P X520 Adapter", -- Dell-branded NIC with an 82599
368  [mod.PCI_ID_X540] = "Ethernet Controller 10-Gigabit X540-AT2",
369  [mod.PCI_ID_XL710] = "Ethernet Controller LX710 for 40GbE QSFP+",
370 }
371 
372 function dev:getName()
373  local id = self:getPciId()
374  return deviceNames[id] or ("unknown NIC (PCI ID %x:%x)"):format(bit.rshift(id, 16), bit.band(id, 0xFFFF))
375 end
376 
377 function mod.getDeviceName(port)
378  return mod.get(port):getName()
379 end
380 
381 function mod.getDevices()
382  local result = {}
383  for i = 0, dpdkc.rte_eth_dev_count() - 1 do
384  local dev = mod.get(i)
385  result[#result + 1] = { id = i, mac = dev:getMacString(i), name = dev:getName(i) }
386  end
387  return result
388 end
389 
390 local function readCtr32(id, addr, last)
391  local val = dpdkc.read_reg32(id, addr)
392  local diff = val - last
393  if diff < 0 then
394  diff = 2^32 + diff
395  end
396  return diff, val
397 end
398 
399 local function readCtr48(id, addr, last)
400  local addrl = addr
401  local addrh = addr + 4
402  -- TODO: we probably need a memory fence here
403  -- however, the intel driver doesn't use a fence here so I guess that should work
404  local h = dpdkc.read_reg32(id, addrh)
405  local l = dpdkc.read_reg32(id, addrl)
406  local h2 = dpdkc.read_reg32(id, addrh) -- check for overflow during read
407  if h2 ~= h then
408  -- overflow during the read
409  -- we can just read the lower value again (1 overflow every 850ms max)
410  l = dpdkc.read_reg32(self.id, 0x00300680)
411  h = h2 -- use the new high value
412  end
413  local val = l + h * 2^32 -- 48 bits, double is fine
414  local diff = val - last
415  if diff < 0 then
416  diff = 2^48 + diff
417  end
418  return diff, val
419 end
420 
421 -- FIXME: only tested on X540, 82599 and 82580 chips
422 -- these functions must be wrapped in a device-specific way
423 -- rx stats
424 local GPRC = 0x00004074
425 local GORCL = 0x00004088
426 local GORCH = 0x0000408C
427 
428 -- tx stats
429 local GPTC = 0x00004080
430 local GOTCL = 0x00004090
431 local GOTCH = 0x00004094
432 
433 local lastGorc = 0
434 local lastUprc = 0
435 local lastMprc = 0
436 local lastBprc = 0
437 
438 --- get the number of packets received since the last call to this function
439 function dev:getRxStats()
440  local devId = self:getPciId()
441  if devId == mod.PCI_ID_XL710 then
442  local uprc, mprc, bprc, gorc
443  uprc, lastUprc = readCtr32(self.id, 0x003005A0, lastUprc)
444  mprc, lastMprc = readCtr32(self.id, 0x003005C0, lastMprc)
445  bprc, lastBprc = readCtr32(self.id, 0x003005E0, lastBprc)
446  gorc, lastGorc = readCtr48(self.id, 0x00300000, lastGorc)
447  return uprc + mprc + bprc, gorc
448  else
449  return dpdkc.read_reg32(self.id, GPRC), dpdkc.read_reg32(self.id, GORCL) + dpdkc.read_reg32(self.id, GORCH) * 2^32
450  end
451 end
452 
453 
454 
455 local lastGotc = 0
456 local lastUptc = 0
457 local lastMptc = 0
458 local lastBptc = 0
459 
460 function dev:getTxStats()
461  local badPkts = tonumber(dpdkc.get_bad_pkts_sent(self.id))
462  local badBytes = tonumber(dpdkc.get_bad_bytes_sent(self.id))
463  -- FIXME: this should really be split up into separate functions/files
464  local devId = self:getPciId()
465  if devId == mod.PCI_ID_XL710 then
466  local uptc, mptc, bptc, gotc
467  uptc, lastUptc = readCtr32(self.id, 0x003009C0, lastUptc)
468  mptc, lastMptc = readCtr32(self.id, 0x003009E0, lastMptc)
469  bptc, lastBptc = readCtr32(self.id, 0x00300A00, lastBptc)
470  gotc, lastGotc = readCtr48(self.id, 0x00300680, lastGotc)
471  return uptc + mptc + bptc - badPkts, gotc - badBytes
472  else
473  -- TODO: check for ixgbe
474  return dpdkc.read_reg32(self.id, GPTC) - badPkts, dpdkc.read_reg32(self.id, GOTCL) + dpdkc.read_reg32(self.id, GOTCH) * 2^32 - badBytes
475  end
476 end
477 
478 
479 --- TODO: figure out how to actually acquire statistics in a meaningful way for dropped packets :/
480 function dev:getRxStatsAll()
481  local stats = ffi.new("struct rte_eth_stats")
482  dpdkc.rte_eth_stats_get(self.id, stats)
483  return stats
484 end
485 
486 local RTTDQSEL = 0x00004904
487 
488 --- Set the tx rate of a queue in MBit/s.
489 --- This sets the payload rate, not to the actual wire rate, i.e. preamble, SFD, and IFG are ignored.
490 --- The X540 and 82599 chips seem to have a hardware bug (?): they seem use the wire rate in some point of the throttling process.
491 --- This causes erratic behavior for rates >= 64/84 * WireRate when using small packets.
492 --- The function is non-linear (not even monotonic) for such rates.
493 --- The function prints a warning if such a rate is configured.
494 --- A simple work-around for this is using two queues with 50% of the desired rate.
495 --- Note that this changes the inter-arrival times as the rate control of both queues is independent.
496 function txQueue:setRate(rate)
497  if self.dev:getPciId() ~= mod.PCI_ID_82599 and self.dev:getPciId() ~= mod.PCI_ID_X540 and self.dev:getPciId() ~= mod.PCI_ID_X520 then
498  error("tx rate control not yet implemented for this NIC")
499  end
500  local speed = self.dev:getLinkStatus().speed
501  if speed <= 0 then
502  print("WARNING: link down, assuming 10 GbE connection")
503  speed = 10000
504  end
505  if rate <= 0 then
506  rate = speed
507  end
508  self.rate = math.min(rate, speed)
509  self.speed = speed
510  local link = self.dev:getLinkStatus()
511  self.speed = link.speed
512  rate = rate / speed
513  -- the X540 and 82599 chips have a hardware bug: they assume that the wire size of an
514  -- ethernet frame is 64 byte when it is actually 84 byte (8 byte preamble/SFD, 12 byte IFG)
515  -- TODO: software fallback for bugged rates and unsupported NICs
516  if rate >= (64 * 64) / (84 * 84) and rate < 1 then
517  print("WARNING: rates with a payload rate >= 64/84% do not work properly with small packets due to a hardware bug, see documentation for details")
518  end
519  if rate <= 0 then
520  error("rate must be > 0")
521  end
522  if rate >= 1 then
523  self:setTxRateRaw(0, true)
524  else
525  self:setTxRateRaw(1 / rate)
526  end
527 end
528 
529 function txQueue:setRateMpps(rate, pktSize)
530  pktSize = pktSize or 60
531  self:setRate(rate * (pktSize + 4) * 8)
532 end
533 
534 local RF_X540_82599 = 0x00004984
535 local RF_ENABLE_BIT = bit.lshift(1, 31)
536 
537 function txQueue:setTxRateRaw(rate, disable)
538  dpdkc.write_reg32(self.id, RTTDQSEL, self.qid)
539  if disable then
540  dpdkc.write_reg32(self.id, RF_X540_82599, 0)
541  return
542  end
543  -- 10.14 fixed-point
544  local rateInt = math.floor(rate)
545  local rateDec = math.floor((rate - rateInt) * 2^14)
546  dpdkc.write_reg32(self.id, RF_X540_82599, bit.bor(bit.lshift(rateInt, 14), rateDec, RF_ENABLE_BIT))
547 end
548 
549 function txQueue:getTxRate()
550  local link = self.dev:getLinkStatus()
551  self.speed = link.speed > 0 and link.speed or 10000
552  dpdkc.write_reg32(self.id, RTTDQSEL, self.qid)
553  local reg = dpdkc.read_reg32(self.id, RF_X540_82599)
554  if reg == 0 then
555  self.rate = nil
556  return self.speed
557  end
558  -- 10.14 fixed-point
559  local rateInt = bit.band(bit.rshift(reg, 14), 0x3FFF)
560  local rateDec = bit.band(reg, 0x3FF)
561  self.rate = (1 / (rateInt + rateDec / 2^14)) * self.speed
562  return self.rate
563 end
564 
565 function txQueue:send(bufs)
566  self.used = true
567  dpdkc.send_all_packets(self.id, self.qid, bufs.array, bufs.size);
568  return bufs.size
569 end
570 
571 function txQueue:start()
572  assert(dpdkc.rte_eth_dev_tx_queue_start(self.id, self.qid) == 0)
573 end
574 
575 function txQueue:stop()
576  assert(dpdkc.rte_eth_dev_tx_queue_stop(self.id, self.qid) == 0)
577 end
578 
579 do
580  local mempool
581  function txQueue:sendWithDelay(bufs, method)
582  self.used = true
583  mempool = mempool or memory.createMemPool(2047, nil, nil, 4095)
584  method = method or "crc"
585  if method == "crc" then
586  dpdkc.send_all_packets_with_delay_bad_crc(self.id, self.qid, bufs.array, bufs.size, mempool)
587  elseif method == "size" then
588  dpdkc.send_all_packets_with_delay_invalid_size(self.id, self.qid, bufs.array, bufs.size, mempool)
589  else
590  errorf("unknown delay method %s", method)
591  end
592  return bufs.size
593  end
594 end
595 
596 --- Restarts all tx queues that were actively used by this task.
597 --- 'Actively used' means that either :send() or :sendWithDelay() was called from the current task.
598 function mod.reclaimTxBuffers()
599  for _, dev in pairs(devices) do
600  for _, queue in pairs(dev.txQueues) do
601  if queue.used then
602  queue:stop()
603  queue:start()
604  end
605  end
606  end
607 end
608 
609 --- Receive packets from a rx queue.
610 --- Returns as soon as at least one packet is available.
611 function rxQueue:recv(bufArray)
612  while dpdk.running() do
613  local rx = dpdkc.rte_eth_rx_burst_export(self.id, self.qid, bufArray.array, bufArray.size)
614  if rx > 0 then
615  return rx
616  end
617  end
618  return 0
619 end
620 
621 function rxQueue:getMacAddr()
622  return ffi.cast("struct mac_address", ffi.C.rte_eth_macaddr_get(self.id))
623 end
624 
625 function txQueue:getMacAddr()
626  return ffi.cast("struct mac_address", ffi.C.rte_eth_macaddr_get(self.id))
627 end
628 
629 function rxQueue:recvAll(bufArray)
630  error("NYI")
631 end
632 
633 --- Receive packets from a rx queue with a timeout.
634 function rxQueue:tryRecv(bufArray, maxWait)
635  maxWait = maxWait or math.huge
636  while maxWait >= 0 do
637  local rx = dpdkc.rte_eth_rx_burst_export(self.id, self.qid, bufArray.array, bufArray.size)
638  if rx > 0 then
639  return rx
640  end
641  maxWait = maxWait - 1
642  -- don't sleep pointlessly
643  if maxWait < 0 then
644  break
645  end
646  dpdk.sleepMicros(1)
647  end
648  return 0
649 end
650 
651 --- Receive packets from a rx queue with a timeout.
652 --- Does not perform a busy wait, this is not suitable for high-throughput applications.
653 function rxQueue:tryRecvIdle(bufArray, maxWait)
654  maxWait = maxWait or math.huge
655  while maxWait >= 0 do
656  local rx = dpdkc.rte_eth_rx_burst_export(self.id, self.qid, bufArray.array, bufArray.size)
657  if rx > 0 then
658  return rx
659  end
660  maxWait = maxWait - 1
661  -- don't sleep pointlessly
662  if maxWait < 0 then
663  break
664  end
665  dpdk.sleepMicrosIdle(1)
666  end
667  return 0
668 end
669 
670 -- export prototypes to extend them in other modules (TODO: use a proper 'class' system with mix-ins or something)
671 mod.__devicePrototype = dev
672 mod.__txQueuePrototype = txQueue
673 mod.__rxQueuePrototype = rxQueue
674 
675 return mod
676 
param n optional(default=2047)
Create a new memory pool.
function rxQueue recv(bufArray)
Receive packets from a rx queue.
function mod disable(port)
Disable the Hardware Crypto Engine.
local ffi
low-level dpdk wrapper
Definition: dpdkc.lua:6
function mod sleepMicrosIdle(t)
Sleep by t microseconds by calling usleep().
function rxQueue tryRecv(bufArray, maxWait)
Receive packets from a rx queue with a timeout.
function mod band(mask1, mask2, result)
Bitwise and.
function mod sleepMicros(t)
Delay by t microseconds.
function mod new(n)
function dev wait(maxWait)
Wait until the device is fully initialized and up to maxWait seconds to establish a link...
function mod bor(mask1, mask2, result)
Bitwise or.
function mempool bufArray(n)
Create a new array of memory buffers (initialized to nil).
local mod
high-level dpdk wrapper
Definition: dpdk.lua:6
function mod config(...)
Configure a device.
function printf(str,...)
Print a formatted string.
function ip4Addr add(val)
Add a number to an IPv4 address in-place.
function txQueue setRate(rate)
Set the tx rate of a queue in MBit/s.
function mod stop()
request all tasks to exit
function mod createMemPool(...)
function rxQueue tryRecvIdle(bufArray, maxWait)
Receive packets from a rx queue with a timeout.
function mod reclaimTxBuffers()
Restarts all tx queues that were actively used by this task.
function dev getRxStatsAll()
TODO: figure out how to actually acquire statistics in a meaningful way for dropped packets :/...
n
Create a new array of memory buffers (initialized to nil).
Definition: memory.lua:76
function errorf(str,...)
Print a formatted error string.
function mod running(extraTime)
Returns false once the app receives SIGTERM or SIGINT, the time set via setRuntime expires...
function dev getRxStats()
get the number of packets received since the last call to this function