diff --git a/src/apps/intel_mp/README.md b/src/apps/intel_mp/README.md index 7847f7eb1b..0d6a564e74 100644 --- a/src/apps/intel_mp/README.md +++ b/src/apps/intel_mp/README.md @@ -6,6 +6,13 @@ be attached to separate instances of the app on different processes. The links are named `input` and `output`. + DIAGRAM: Intel + +-------+ + | | + input ---->* Intel *----> output + | | + +-------+ + ## Caveats If attaching multiple processes to a single NIC, performance appears @@ -13,6 +20,23 @@ better with `engine.busywait = false`. The `intel_mp.Intel` app can drive an Intel 82599 NIC at 14 million pps. +— Method **Intel:get_rxstats** + +Returns a table with the following keys: + +* `counter_id` - Counter id +* `packets` - Number of packets received +* `dropped` - Number of packets dropped +* `bytes` - Total bytes received + +— Method **Intel:get_txstats** + +Returns a table with the following keys: + +* `counter_id` - Counter id +* `packets` - Number of packets sent +* `bytes` - Total bytes sent + ## Configuration — Key **pciaddr** @@ -27,11 +51,87 @@ specified but assumed to be broadly applicable. — Key **rxq** -*Optional*. The receive queue to attach to, numbered from 0. +*Optional*. The receive queue to attach to, numbered from 0. The default is 0. +When VMDq is enabled, this number is used to index a queue (0 or 1) +for the selected pool. Passing `"off"` will disable the receive queue. — Key **txq** -*Optional*. The transmit queue to attach to, numbered from 0. +*Optional*. The transmit queue to attach to, numbered from 0. The default is 0. +Passing `"off"` will disable the transmit queue. + +— Key **vmdq** + +*Optional*. A boolean parameter that specifies whether VMDq (Virtual Machine +Device Queues) is enabled. When VMDq is enabled, each instance of the driver +is associated with a *pool* that can be assigned a MAC address or VLAN tag. +Packets are delivered to pools that match the corresponding MACs or VLAN tags. +Each pool may be associated with several receive and transmit queues. + +For a given NIC, all driver instances should have this parameter either +enabled or disabled uniformly. If this is enabled, *macaddr* must be +specified. + +— Key **poolnum** + +*Optional*. The VMDq pool to associate with, numbered from 0. The default +is to select a pool number automatically. + +— Key **macaddr** + +*Optional*. The MAC address to use as a string. The default is a wild-card +(i.e., accept all packets). + +— Key **vlan** +*Optional*. A twelve-bit integer (0-4095). If set, incoming packets from +other VLANs are dropped and outgoing packets are tagged with a VLAN header. + +— Key **mirror** + +*Optional*. A table. If set, this app will receive copies of all selected +packets on the physical port. The selection is configured by setting keys +of the *mirror* table. Either *mirror.pool* or *mirror.port* may be set. + +If *mirror.pool* is `true` all pools defined on this physical port are +mirrored. If *mirror.pool* is an array of pool numbers then the specified +pools are mirrored. + +If *mirror.port* is one of "in", "out" or "inout" all incoming and/or +outgoing packets on the port are mirrored respectively. Note that this +does not include internal traffic which does not enter or exit through +the physical port. + +— Key **rxcounter** + +— Key **txcounter** + +*Optional*. Four bit integers (0-15). If set, incoming/outgoing packets +will be counted in the selected statistics counter respectively. Multiple +apps can share a counter. To retrieve counter statistics use +`Intel:get_rxstats()` and `Intel:get_txstats()`. + +— Key **rate_limit** + +*Optional*. Number. Limits the maximum Mbit/s to transmit. Default is 0 +which means no limit. Only applies to outgoing traffic. + +— Key **priority** + +*Optional*. Floating point number. Weight for the *round-robin* algorithm +used to arbitrate transmission when *rate_limit* is not set or adds up to +more than the line rate of the physical port. Default is 1.0 (scaled to +the geometric middle of the scale which goes from 1/128 to 128). The +absolute value is not relevant, instead only the ratio between competing +apps controls their respective bandwidths. Only applies to outgoing +traffic. + +For example, if two apps without *rate_limit* set have the same +*priority*, both get the same output bandwidth. If the priorities are +3.0/1.0, the output bandwidth is split 75%/25%. Likewise, 1.0/0.333 or +1.5/0.5 yield the same result. + +Note that even a low-priority app can use the whole line rate unless other +(higher priority) apps are using up the available bandwidth. — Key **rsskey** @@ -92,3 +192,7 @@ Each chipset supports a differing number of receive / transmit queues: * Intel82599 supports 16 receive and 16 transmit queues, 0-15 * Intel1g i350 supports 8 receive and 8 transmit queues, 0-7 * Intel1g i210 supports 4 receive and 4 transmit queues, 0-3 + +The Intel82599 supports both VMDq and RSS with 32/64 pools and 4/2 RSS queues for +each pool. This driver only supports configurations with 64 pools/2 queues. +While the i350 supports VMDq, this driver does not currently support it. diff --git a/src/apps/intel_mp/intel_mp.lua b/src/apps/intel_mp/intel_mp.lua index e1760882c4..4daf58694a 100644 --- a/src/apps/intel_mp/intel_mp.lua +++ b/src/apps/intel_mp/intel_mp.lua @@ -1,4 +1,4 @@ --- intel1g: Device driver app for Intel 1G and 10G network cards +-- intel_mp: Device driver app for Intel 1G and 10G network cards -- It supports -- - Intel1G i210 and i350 based 1G network cards -- - Intel82599 82599 based 10G network cards @@ -25,7 +25,8 @@ local register = require("lib.hardware.register") local counter = require("core.counter") local macaddress = require("lib.macaddress") local shm = require("core.shm") -local counter = require("core.counter") +local S = require("syscall") +local transmit, receive, empty = link.transmit, link.receive, link.empty -- It's not clear what address to use for EEMNGCTL_i210 DPDK PMD / linux e1000 -- both use 1010 but the docs say 12030 @@ -62,17 +63,29 @@ ALLRXDCTL 0x01028 +0x40*0..63 RW Receive Descriptor Control ALLRXDCTL 0x0D028 +0x40*64..127 RW Receive Descriptor Control DAQF 0x0E200 +0x04*0..127 RW Destination Address Queue Filter FTQF 0x0E600 +0x04*0..127 RW Five Tuple Queue Filter +ETQF 0x05128 +0x04*0..7 RW EType Queue Filter +ETQS 0x0EC00 +0x04*0..7 RW EType Queue Select MPSAR 0x0A600 +0x04*0..255 RW MAC Pool Select Array PFUTA 0X0F400 +0x04*0..127 RW PF Unicast Table Array PFVLVF 0x0F100 +0x04*0..63 RW PF VM VLAN Pool Filter PFVLVFB 0x0F200 +0x04*0..127 RW PF VM VLAN Pool Filter Bitmap +PFMRCTL 0x0F600 +0x04*0..3 RW PF Mirror Rule Control +PFMRVLAN 0x0F610 +0x04*0..7 RW PF Mirror Rule VLAN +PFMRVM 0x0F630 +0x04*0..7 RW PF Mirror Rule Pool +PFVFRE 0x051E0 +0x04*0..1 RW PF VF Receive Enable +PFVFTE 0x08110 +0x04*0..1 RW PF VF Transmit Enable +PFVMTXSW 0x05180 +0x04*0..1 RW PF VM Tx Switch Loopback Enable +PFVFSPOOF 0x08200 +0x04*0..7 RW PF VF Anti Spoof Control +PFVMVIR 0x08000 +0x04*0..63 RW PF VM VLAN Insert Register +PFVML2FLT 0x0F000 +0x04*0..63 RW PF VM L2 Control Register QPRC 0x01030 +0x40*0..15 RC Queue Packets Received Count QPRDC 0x01430 +0x40*0..15 RC Queue Packets Received Drop Count QBRC64 0x01034 +0x40*0..15 RC64 Queue Bytes Received Count -QPTC 0x08680 +0x40*0..15 RC Queue Packets Transmitted Count -QBTC64 0x08700 +0x40*0..15 RC64 Queue Bytes Transmitted Count Low +QPTC 0x08680 +0x04*0..15 RC Queue Packets Transmitted Count +QBTC64 0x08700 +0x08*0..15 RC64 Queue Bytes Transmitted Count Low SAQF 0x0E000 +0x04*0..127 RW Source Address Queue Filter SDPQF 0x0E400 +0x04*0..127 RW Source Destination Port Queue Filter +PSRTYPE 0x0EA00 +0x04*0..63 RW Packet Split Receive Type Register RAH 0x0A204 +0x08*0..127 RW Receive Address High RAL 0x0A200 +0x08*0..127 RW Receive Address Low RAL64 0x0A200 +0x08*0..127 RW64 Receive Address Low and High @@ -81,11 +94,13 @@ RTTDT2C 0x04910 +0x04*0..7 RW DCB Transmit Descriptor Plane T2 Config RTTPT2C 0x0CD20 +0x04*0..7 RW DCB Transmit Packet Plane T2 Config RTRPT4C 0x02140 +0x04*0..7 RW DCB Receive Packet Plane T4 Config RXPBSIZE 0x03C00 +0x04*0..7 RW Receive Packet Buffer Size +RQSMR 0x02300 +0x04*0..31 RW Receive Queue Statistic Mapping Registers TQSM 0x08600 +0x04*0..31 RW Transmit Queue Statistic Mapping Registers TXPBSIZE 0x0CC00 +0x04*0..7 RW Transmit Packet Buffer Size TXPBTHRESH 0x04950 +0x04*0..7 RW Tx Packet Buffer Threshold VFTA 0x0A000 +0x04*0..127 RW VLAN Filter Table Array QPRDC 0x01430 +0x40*0..15 RC Queue Packets Received Drop Count +FCRTH 0x03260 +0x40*0..7 RW Flow Control Receive Threshold High ]], inherit = "gbl", rxq = [[ @@ -124,11 +139,19 @@ MFLCN 0x04294 - RW MAC Flow Control Register MRQC 0x0EC80 - RW Multiple Receive Queues Command Register MTQC 0x08120 - RW Multiple Transmit Queues Command Register PFVTCTL 0x051B0 - RW PF Virtual Control Register +PFQDE 0x02F04 - RW PF Queue Drop Enable Register +PFDTXGSWC 0x08220 - RW PF DMA Tx General Switch Control RDRXCTL 0x02F00 - RW Receive DMA Control Register +RTRPCS 0x02430 - RW DCB Receive Packet plane Control and Status +RTTDCS 0x04900 - RW DCB Transmit Descriptor Plane Control and Status +RTTPCS 0x0CD00 - RW DCB Transmit Packet Plane Control and Status RTRUP2TC 0x03020 - RW DCB Receive Use rPriority to Traffic Class RTTUP2TC 0x0C800 - RW DCB Transmit User Priority to Traffic Class +RTTDQSEL 0x04904 - RW DCB Transmit Descriptor Plane Queue Select +RTTDT1C 0x04908 - RW DCB Transmit Descriptor Plane T1 Config RTTBCNRC 0x04984 - RW DCB Transmit Rate-Scheduler Config RXCSUM 0x05000 - RW Receive Checksum Control +RFCTL 0x05008 - RW Receive Filter Control Register RXCTRL 0x03000 - RW Receive Control RXDGPC 0x02F50 - RC DMA Good Rx Packet Counter RXDSTATCTRL 0x02F40 - RW Rx DMA Statistic Counter Control @@ -252,8 +275,17 @@ Intel = { config = { pciaddr = {required=true}, ndescriptors = {default=2048}, - txq = {}, - rxq = {}, + vmdq = {default=false}, + macaddr = {}, + poolnum = {}, + vlan = {}, + mirror = {}, + rxcounter = {}, + txcounter = {}, + rate_limit = {default=0}, + priority = {default=1.0}, + txq = {default=0}, + rxq = {default=0}, mtu = {default=9014}, linkup_wait = {default=120}, linkup_wait_recheck = {default=0.1}, @@ -276,7 +308,7 @@ byPciID = { } -- The `driver' variable is used as a reference to the driver class in --- order to interchangably use NIC drivers. +-- order to interchangeably use NIC drivers. driver = Intel function Intel:new (conf) @@ -284,17 +316,28 @@ function Intel:new (conf) r = {}, pciaddress = conf.pciaddr, path = pci.path(conf.pciaddr), - -- falling back to defaults for selftest bypassing config.app - ndesc = conf.ndescriptors or self.config.ndescriptors.default, + ndesc = conf.ndescriptors, txq = conf.txq, rxq = conf.rxq, - mtu = conf.mtu or self.config.mtu.default, - linkup_wait = conf.linkup_wait or self.config.linkup_wait.default, - linkup_wait_recheck = - conf.linkup_wait_recheck or self.config.linkup_wait_recheck.default, - wait_for_link = conf.wait_for_link + mtu = conf.mtu, + linkup_wait = conf.linkup_wait, + linkup_wait_recheck = conf.linkup_wait_recheck, + wait_for_link = conf.wait_for_link, + vmdq = conf.vmdq, + poolnum = conf.poolnum, + macaddr = conf.macaddr, + vlan = conf.vlan, + want_mirror = conf.mirror, + rxcounter = conf.rxcounter, + txcounter = conf.txcounter, + rate_limit = conf.rate_limit, + priority = conf.priority } + -- txq/rxq have defaults so nil can't represent off state + if conf.txq == "off" then self.txq = nil end + if conf.rxq == "off" then self.rxq = nil end + local vendor = lib.firstline(self.path .. "/vendor") local device = lib.firstline(self.path .. "/device") local byid = byPciID[tonumber(device)] @@ -308,12 +351,22 @@ function Intel:new (conf) self.base, self.fd = pci.map_pci_memory_unlocked(self.pciaddress, 0) self.master = self.fd:flock("ex, nb") + -- this needs to happen before register loading for rxq/txq + self:select_pool() + self:load_registers(byid.registers) self:init() self.fd:flock("sh") + self:check_vmdq() self:init_tx_q() self:init_rx_q() + self:set_MAC() + self:set_VLAN() + self:set_mirror() + self:set_rxstats() + self:set_txstats() + self:set_tx_rate() -- Initialize per app statistics counter.set(self.shm.mtu, self.mtu) @@ -385,6 +438,15 @@ function Intel:init_rx_q () local rxdesc_ring_t = ffi.typeof("$[$]", rxdesc_t, self.ndesc) self.rxdesc = ffi.cast(ffi.typeof("$&", rxdesc_ring_t), memory.dma_alloc(ffi.sizeof(rxdesc_ring_t))) + + -- VMDq pool state (4.6.10.1.4) + if self.vmdq then + -- packet splitting none, enable 2 RSS queues per pool + self.r.PSRTYPE[self.poolnum](bits { RQPL=29 }) + -- multicast promiscuous, broadcast accept, accept untagged pkts + self.r.PFVML2FLT[self.poolnum]:set(bits { MPE=28, BAM=27, AUPE=24 }) + end + -- Receive state self.r.RDBAL(tophysical(self.rxdesc) % 2^32) self.r.RDBAH(tophysical(self.rxdesc) / 2^32) @@ -398,13 +460,18 @@ function Intel:init_rx_q () end self.r.SRRCTL(0) self.r.SRRCTL:set(bits { - -- Set packet buff size to 0b1010 kbytes - BSIZEPACKET1 = 1, - BSIZEPACKET3 = 3, + -- Set packet buff size to 0b10000 kbytes (max) + BSIZEPACKET4 = 4, -- Drop packets when no descriptors Drop_En = self:offset("SRRCTL", "Drop_En") }) self:lock_sw_sem() + + -- enable VLAN tag stripping in VMDq mode + if self.vmdq then + self.r.RXDCTL:set(bits { VME = 30 }) + end + self.r.RXDCTL:set( bits { Enable = 25 }) self.r.RXDCTL:wait( bits { Enable = 25 }) C.full_memory_barrier() @@ -414,6 +481,9 @@ function Intel:init_rx_q () if self.driver == "Intel82599" then self.r.RXCTRL:set(bits{ RXEN=0 }) self.r.DCA_RXCTRL:clr(bits{RxCTRL=12}) + if self.vmdq then + self.r.PFVFRE[math.floor(self.poolnum/32)]:set(bits{VFRE=self.poolnum%32}) + end elseif self.driver == "Intel1g" then self.r.RCTL:set(bits { RXEN = 1 }) end @@ -447,6 +517,17 @@ function Intel:init_tx_q () -- 4.5.10 self.r.TDBAH(tophysical(self.txdesc) / 2^32) self.r.TDLEN(self.ndesc * ffi.sizeof(txdesc_t)) + -- for VMDq need some additional pool configs + if self.vmdq then + self.r.RTTDQSEL(self.poolnum) + -- set baseline value for credit refill for tx bandwidth algorithm + self.r.RTTDT1C(0x80) + -- enables packet Tx for this VF's pool + self.r.PFVFTE[math.floor(self.poolnum/32)]:set(bits{VFTE=self.poolnum%32}) + -- enable TX loopback + self.r.PFVMTXSW[math.floor(self.poolnum/32)]:clr(bits{LLE=self.poolnum%32}) + end + if self.r.DMATXCTL then self.r.DMATXCTL:set(bits { TE = 0 }) self.r.TXDCTL:set(bits{SWFLSH=26, hthresh=8} + 32) @@ -487,20 +568,22 @@ end function Intel:push () if not self.txq then return end local li = self.input["input"] - assert(li, "intel1g:push: no input link") - - while not link.empty(li) and self:ringnext(self.tdt) ~= self.tdh do - local p = link.receive(li) - if p.length > self.mtu then - packet.free(p) - counter.add(self.shm.txdrop) - else - self.txdesc[self.tdt].address = tophysical(p.data) - self.txdesc[self.tdt].flags = - bor(p.length, self.txdesc_flags, lshift(p.length+0ULL, 46)) - self.txqueue[self.tdt] = p - self.tdt = self:ringnext(self.tdt) - end + if li == nil then return end +-- assert(li, "intel_mp:push: no input link") + + while not empty(li) and self:ringnext(self.tdt) ~= self.tdh do + local p = receive(li) + -- NB: see comment in intel10g for why this is commented out, + -- the rest of the loop body goes in an else branch + --if p.length > self.mtu then + -- packet.free(p) + -- counter.add(self.shm.txdrop) + --end + self.txdesc[self.tdt].address = tophysical(p.data) + self.txdesc[self.tdt].flags = + bor(p.length, self.txdesc_flags, lshift(p.length+0ULL, 46)) + self.txqueue[self.tdt] = p + self.tdt = self:ringnext(self.tdt) end -- Reclaim transmit contexts local cursor = self.tdh @@ -514,18 +597,26 @@ function Intel:push () cursor = self:ringnext(cursor) end self.r.TDT(self.tdt) + + -- same code as in pull, but we only call it in case the rxq + -- is disabled for this app + if self.rxq and self.output["output"] then return end + if self.run_stats and self.sync_timer() then + self:sync_stats() + end end function Intel:pull () if not self.rxq then return end local lo = self.output["output"] - assert(lo, "intel1g: output link required") + if lo == nil then return end +-- assert(lo, "intel_mp:pull: output link required") local pkts = 0 while band(self.rxdesc[self.rdt].status, 0x01) == 1 and pkts < engine.pull_npackets do local p = self.rxqueue[self.rdt] p.length = self.rxdesc[self.rdt].length - link.transmit(lo, p) + transmit(lo, p) local np = packet.allocate() self.rxqueue[self.rdt] = np @@ -568,6 +659,12 @@ function Intel:rss_key () self.r.RSSRK[i](math.random(2^32)) end end + +-- Set RSS redirection table, which has 64 * 2 entries which contain +-- RSS indices, the lower 4 bits (or fewer) of which are used to +-- select an RSS queue. +-- +-- Also returns the current state of the redirection table function Intel:rss_tab (newtab) local current = {} local pos = 0 @@ -585,10 +682,6 @@ function Intel:rss_tab (newtab) return current end function Intel:rss_tab_build () - -- noop if rss is not enabled - local b = bits { RSS = self:offset("MRQC", "RSS") } - if bit.band(self.r.MRQC(), b) ~= b then return end - local tab = {} for i=0,self.max_q-1,1 do if band(self.r.ALLRXDCTL[i](), bits { Enable = 25 }) > 0 then @@ -628,15 +721,23 @@ function Intel:stop () --TXDCTL[n].SWFLSH and wait --wait until tdh == tdt --wait on rxd[tdh].status = dd + self:discard_unsent_packets() self.r.TXDCTL(0) self.r.TXDCTL:wait(bits { ENABLE = 25 }, 0) - for i = 0, self.ndesc-1 do - if self.txqueue[i] then - packet.free(self.txqueue[i]) - self.txqueue[i] = nil - end + end + if self.vmdq then + self:unset_MAC() + self:unset_VLAN() + self:unset_mirror() + if self.poolfd then + -- we need to explicitly unlock this in case multiple instances + -- are running on the same process and we can't rely on process + -- termination to free the lock + self.poolfd:flock("un") + self.poolfd:close() end end + self:unset_tx_rate() if self.fd:flock("nb, ex") then self.r.CTRL:clr( bits { SETLINKUP = 6 } ) --self.r.CTRL_EXT:clear( bits { DriverLoaded = 28 }) @@ -648,6 +749,20 @@ function Intel:stop () end end +function Intel:discard_unsent_packets () + local old_tdt = self.tdt + self.tdt = self.r.TDT() + self.tdh = self.r.TDH() + self.r.TDT(self.tdh) + while old_tdt ~= self.tdh do + old_tdt = band(old_tdt - 1, self.ndesc - 1) + packet.free(self.txqueue[old_tdt]) + self.txdesc[old_tdt].address = -1 + self.txdesc[old_tdt].flags = 0 + end + self.tdt = self.tdh +end + function Intel:sync_stats () local set, stats = counter.set, self.stats set(stats.speed, self:link_speed()) @@ -666,11 +781,209 @@ function Intel:sync_stats () set(stats.txdrop, self:txdrop()) set(stats.txerrors, self:txerrors()) set(stats.rxdmapackets, self:rxdmapackets()) - for name, register in pairs(self.queue_stats) do + for idx = 1, #self.queue_stats, 2 do + local name, register = self.queue_stats[idx], self.queue_stats[idx+1] set(stats[name], register()) end end +-- set MAC address (4.6.10.1.4) +function Intel:set_MAC () + if not self.macaddr then return end + local mac = macaddress:new(self.macaddr) + self:add_receive_MAC(mac) + self:set_transmit_MAC(mac) +end + +function Intel:add_receive_MAC (mac) + local mac_index + + -- scan to see if the MAC is already recorded or find the + -- first free MAC index + -- + -- the lock protects the critical section so that driver apps on + -- separate processes do not use conflicting registers + self:lock_sw_sem() + for idx=1, self.max_mac_addr do + local valid = self.r.RAH[idx]:bits(31, 1) + + if valid == 0 then + mac_index = idx + self.r.RAL[mac_index](mac:subbits(0,32)) + self.r.RAH[mac_index](bits({AV=31},mac:subbits(32,48))) + break + else + if self.r.RAL[idx]() == mac:subbits(0, 32) and + self.r.RAH[idx]:bits(0, 15) == mac:subbits(32, 48) then + mac_index = idx + break + end + end + end + self:unlock_sw_sem() + + assert(mac_index, "Max number of MAC addresses reached") + + -- associate MAC with the app's VMDq pool + self:enable_MAC_for_pool(mac_index) +end + +function Intel:set_transmit_MAC (mac) + local poolnum = self.poolnum or 0 + self.r.PFVFSPOOF[math.floor(poolnum/8)]:set(bits{MACAS=poolnum%8}) +end + +-- set VLAN for the driver instance +function Intel:set_VLAN () + local vlan = self.vlan + if not vlan then return end + assert(vlan>=0 and vlan<4096, "bad VLAN number") + self:add_receive_VLAN(vlan) + self:set_tag_VLAN(vlan) +end + +function Intel:add_receive_VLAN (vlan) + assert(vlan>=0 and vlan<4096, "bad VLAN number") + local vlan_index, first_empty + + -- works the same as add_receive_MAC + self:lock_sw_sem() + for idx=0, self.max_vlan-1 do + local valid = self.r.PFVLVF[idx]:bits(31, 1) + + if valid == 0 then + if not first_empty then + first_empty = idx + end + elseif self.r.PFVLVF[idx]:bits(0, 11) == vlan then + vlan_index = idx + break + end + end + self:unlock_sw_sem() + + if not vlan_index and first_empty then + vlan_index = first_empty + self.r.VFTA[math.floor(vlan/32)]:set(bits{Ena=vlan%32}) + self.r.PFVLVF[vlan_index](bits({Vl_En=31},vlan)) + end + + assert(vlan_index, "Max number of VLAN IDs reached") + + self.r.PFVLVFB[2*vlan_index + math.floor(self.poolnum/32)] + :set(bits{PoolEna=self.poolnum%32}) +end + +function Intel:set_tag_VLAN (vlan) + local poolnum = self.poolnum or 0 + self.r.PFVFSPOOF[math.floor(poolnum/8)]:set(bits{VLANAS=poolnum%8+8}) + -- set Port VLAN ID & VLANA to always add VLAN tag + -- TODO: on i350 it's the VMVIR register + self.r.PFVMVIR[poolnum](bits({VLANA=30}, vlan)) +end + +function Intel:unset_VLAN () + local r = self.r + local offs, mask = math.floor(self.poolnum/32), bits{PoolEna=self.poolnum%32} + + for vln_ndx = 0, 63 do + if band(r.PFVLVFB[2*vln_ndx+offs](), mask) ~= 0 then + -- found a vlan this pool belongs to + r.PFVLVFB[2*vln_ndx+offs]:clr(mask) + if r.PFVLVFB[2*vln_ndx+offs]() == 0 then + -- it was the last pool of the vlan + local vlan = tonumber(band(r.PFVLVF[vln_ndx](), 0xFFF)) + r.PFVLVF[vln_ndx](0x0) + r.VFTA[math.floor(vlan/32)]:clr(bits{Ena=vlan%32}) + end + end + end +end + +function Intel:set_mirror () + if not self.want_mirror then return end + want_mirror = self.want_mirror + + -- set MAC promiscuous + self.r.PFVML2FLT[self.poolnum]:set(bits{ + AUPE=24, ROMPE=25, ROPE=26, BAM=27, MPE=28}) + + -- pick one of a limited (4) number of mirroring rules + for idx=0, 3 do + -- check if no mirroring enable bits (3:0) are set + -- (i.e., this rule is unused and available) + if self.r.PFMRCTL[idx]:bits(0, 4) == 0 then + mirror_ndx = idx + break + -- there's already a rule for this pool, overwrite + elseif self.r.PFMRCTL[idx]:bits(8, 5) == self.poolnum then + mirror_ndx = idx + break + end + end + + assert(mirror_ndx, "Max number of mirroring rules reached") + + local mirror_rule = 0ULL + + -- mirror some or all pools + if want_mirror.pool then + mirror_rule = bor(bits{VPME=0}, mirror_rule) + if want_mirror.pool == true then -- mirror all pools + self.r.PFMRVM[mirror_ndx](0xFFFFFFFF) + self.r.PFMRVM[mirror_ndx+4](0xFFFFFFFF) + elseif type(want_mirror.pool) == 'table' then + local bm0 = self.r.PFMRVM[mirror_ndx]() + local bm1 = self.r.PFMRVM[mirror_ndx+4]() + for _, pool in ipairs(want_mirror.pool) do + if pool <= 64 then + bm0 = bor(lshift(1, pool), bm0) + else + bm1 = bor(lshift(1, pool-64), bm1) + end + end + self.r.PFMRVM[mirror_ndx](bm0) + self.r.PFMRVM[mirror_ndx+4](bm1) + end + end + + -- mirror hardware port + if want_mirror.port then + if want_mirror.port == true or + want_mirror.port == 'in' or + want_mirror.port == 'inout' then + mirror_rule = bor(bits{UPME=1}, mirror_rule) + end + if want_mirror.port == true or + want_mirror.port == 'out' or + want_mirror.port == 'inout' then + mirror_rule = bor(bits{DPME=2}, mirror_rule) + end + end + + -- TODO: implement VLAN mirroring + + if mirror_rule ~= 0 then + mirror_rule = bor(mirror_rule, lshift(self.poolnum, 8)) + self.r.PFMRCTL[mirror_ndx]:set(mirror_rule) + end +end + +function Intel:unset_mirror () + for rule_i = 0, 3 do + -- check if any mirror rule points here + local rule_dest = band(bit.rshift(self.r.PFMRCTL[rule_i](), 8), 63) + local bits = band(self.r.PFMRCTL[rule_i](), 0x07) + if bits ~= 0 and rule_dest == self.poolnum then + self.r.PFMRCTL[rule_i](0x0) -- clear rule + self.r.PFMRVLAN[rule_i](0x0) -- clear VLANs mirrored + self.r.PFMRVLAN[rule_i+4](0x0) + self.r.PFMRVM[rule_i](0x0) -- clear pools mirrored + self.r.PFMRVM[rule_i+4](0x0) + end + end +end + function Intel:rxpackets () return self.r.GPRC() end function Intel:txpackets () return self.r.GPTC() end function Intel:rxmcast () return self.r.MPRC() + self.r.BPRC() end @@ -687,6 +1000,8 @@ Intel1g.offsets = { RSS = 1 } } +Intel1g.max_mac_addr = 15 +Intel1g.max_vlan = 8 function Intel1g:init_phy () -- 4.3.1.4 PHY Reset self.r.MANC:wait(bits { BLK_Phy_Rst_On_IDE = 18 }, 0) @@ -771,14 +1086,14 @@ function Intel1g:init () end function Intel1g:link_status () - local mask = bits { Link_up = 1 } + local mask = lshift(1, 1) return bit.band(self.r.STATUS(), mask) == mask end function Intel1g:link_speed () return ({10000,100000,1000000,1000000})[1+bit.band(bit.rshift(self.r.STATUS(), 6),3)] end function Intel1g:promisc () - return band(self.r.RCTL(), bits{UPE=3}) ~= 0ULL + return band(self.r.RCTL(), lshift(1, 3)) ~= 0ULL end function Intel1g:rxbytes () return self.r.GORCH()*2^32 + self.r.GORCL() end function Intel1g:rxdrop () return self.r.MPC() + self.r.RNBC() end @@ -807,12 +1122,61 @@ function Intel1g:init_queue_stats (frame) for i=0,self.max_q-1 do for k,v in pairs(perqregs) do local name = "q" .. i .. "_" .. k - self.queue_stats[name] = self.r[v][i] + table.insert(self.queue_stats, name) + table.insert(self.queue_stats, self.r[v][i]) frame[name] = {counter} end end end +function Intel1g:get_rxstats () + assert(self.rxq, "cannot retrieve rxstats without rxq") + local frame = shm.open_frame("pci/"..self.pciaddress) + local rxc = self.rxq + return { + counter_id = rxc, + packets = counter.read(frame["q"..rxc.."_rxpackets"]), + dropped = counter.read(frame["q"..rxc.."_rxdrops"]), + bytes = counter.read(frame["q"..rxc.."_rxbytes"]) + } +end + +function Intel1g:get_txstats () + assert(self.txq, "cannot retrieve rxstats without txq") + local frame = shm.open_frame("pci/"..self.pciaddress) + local txc = self.txq + return { + counter_id = txc, + packets = counter.read(frame["q"..txc.."_txpackets"]), + bytes = counter.read(frame["q"..txc.."_txbytes"]) + } +end + +-- noop because 1g NICs have per-queue counters that aren't +-- configurable +function Intel1g:set_rxstats () return end +function Intel1g:set_txstats () return end + +function Intel1g:check_vmdq () return end +function Intel1g:vmdq_enable () + error("unimplemented") +end +function Intel1g:select_pool () return end + +function Intel1g:enable_MAC_for_pool(mac_index) + self.r.RAH[mac_index]:set(bits { Ena = 18 + self.poolnum }) +end + +function Intel1g:unset_MAC () + local msk = bits { Ena = 18 + self.poolnum } + for mac_index = 0, self.max_mac_addr do + pf.r.RAH[mac_index]:clr(msk) + end +end + +function Intel1g:set_tx_rate () return end +function Intel1g:unset_tx_rate () return end + Intel82599.driver = "Intel82599" Intel82599.offsets = { SRRCTL = { @@ -822,8 +1186,15 @@ Intel82599.offsets = { RSS = 0 } } +Intel82599.max_mac_addr = 127 +Intel82599.max_vlan = 64 + +-- 1010 -> 32 pools, 4 RSS queues each +-- 1011 -> 64 pools, 2 RSS queues each +Intel82599.mrqc_bits = 0xB + function Intel82599:link_status () - local mask = bits { Link_up = 30 } + local mask = lshift(1, 30) return bit.band(self.r.LINKS(), mask) == mask end function Intel82599:link_speed () @@ -834,7 +1205,7 @@ function Intel82599:link_speed () or 1000000 -- 100 Mb/s end function Intel82599:promisc () - return band(self.r.FCTRL(), bits{UPE=9}) ~= 0ULL + return band(self.r.FCTRL(), lshift(1, 9)) ~= 0ULL end function Intel82599:rxbytes () return self.r.GORC64() end function Intel82599:rxdrop () return self.r.QPRDC[0]() end @@ -861,7 +1232,8 @@ function Intel82599:init_queue_stats (frame) for i=0,15 do for k,v in pairs(perqregs) do local name = "q" .. i .. "_" .. k - self.queue_stats[name] = self.r[v][i] + table.insert(self.queue_stats, name) + table.insert(self.queue_stats, self.r[v][i]) frame[name] = {counter} end end @@ -931,12 +1303,12 @@ function Intel82599:init () self.r.VLNCTRL(0x8100) -- explicity set default self.r.RXCSUM(0) -- turn off all checksum offload - self.r.RXPBSIZE[0]:bits(10,19, 0x200) - self.r.TXPBSIZE[0]:bits(10,19, 0xA0) + self.r.RXPBSIZE[0]:bits(10,10, 0x200) + self.r.TXPBSIZE[0]:bits(10,10, 0xA0) self.r.TXPBTHRESH[0](0xA0) for i=1,7 do - self.r.RXPBSIZE[i]:bits(10,19, 0) - self.r.TXPBSIZE[i]:bits(10,19, 0) + self.r.RXPBSIZE[i]:bits(10,10, 0) + self.r.TXPBSIZE[i]:bits(10,10, 0) self.r.TXPBTHRESH[i](0) end @@ -953,6 +1325,8 @@ function Intel82599:init () self.r.RTTDT2C[i](0) self.r.RTTPT2C[i](0) self.r.RTRPT4C[i](0) + self.r.ETQF[i](0) + self.r.ETQS[i](0) end self.r.HLREG0(bits{ @@ -965,9 +1339,222 @@ function Intel82599:init () self.r.CTRL_EXT:set(bits {NS_DIS = 1}) self:rss_enable() + + if self.vmdq then + self:vmdq_enable() + end + self:unlock_sw_sem() end +-- Implements various status checks related to VMDq configuration. +-- Also checks that the main process used the same VMDq setting if +-- this is a worker process +function Intel82599:check_vmdq () + if not self.vmdq then + assert(not self.macaddr, "VMDq must be set to use MAC address") + assert(not self.mirror, "VMDq must be set to specify mirroring rules") + + if not self.master then + assert(self.r.MRQC:bits(0, 4) ~= self.mrqc_bits, + "VMDq was set by the main process for this NIC") + end + else + assert(self.driver == "Intel82599", "VMDq only supported on 82599") + assert(self.macaddr, "MAC address must be set in VMDq mode") + assert(self.poolnum < 64, "Pool overflow: Intel 82599 supports up to 64 VMDq pools") + + if not self.master then + assert(self.r.MRQC:bits(0, 4) == self.mrqc_bits, + "VMDq not set by the main process for this NIC") + end + end +end + +-- enable VMDq mode, see 4.6.10.1 +-- follows the configuration flow in 4.6.11.3.3 +-- (should only be called on the master instance) +function Intel82599:vmdq_enable () + -- must be set prior to setting MTQC (7.2.1.2.1) + self.r.RTTDCS:set(bits { ARBDIS=6 }) + + self.r.MRQC:bits(0, 4, self.mrqc_bits) + + -- TODO: not sure this is needed, but it's in intel10g + -- disable RSC (7.11) + self.r.RFCTL:set(bits { RSC_Dis=5 }) + + -- 128 Tx Queues, 64 VMs (4.6.11.3.3 and 8.2.3.9.15) + self.r.MTQC(bits { VT_Ena=1, Num_TC_OR_Q=2 }) + + -- enable virtualization, replication enabled, disable default pool + self.r.PFVTCTL(bits { VT_Ena=0, Rpl_En=30, DisDefPool=29 }) + + -- enable VMDq Tx to Rx loopback + self.r.PFDTXGSWC:set(bits { LBE=0 }) + + -- needs to be set for loopback (7.10.3.4) + self.r.FCRTH[0](0x10000) + + -- enable vlan filter (4.6.7, 7.1.1.2) + self.r.VLNCTRL:set(bits { VFE=30 }) + + -- RTRUP2TC/RTTUP2TC cleared above in init + + -- DMA TX TCP max allowed size requests (set to 1MB) + self.r.DTXMXSZRQ(0xFFF) + + -- disable PFC, enable legacy control flow + self.r.MFLCN(bits { RFCE=3 }) + self.r.FCCFG(bits { TFCE=3 }) + + -- RTTDT2C, RTTPT2C, RTRPT4C cleared above in init() + + -- QDE bit = 0 for all queues + for i = 0, 127 do + self.r.PFQDE(bor(lshift(1,16), lshift(i,8))) + end + + -- clear RTTDT1C, PFVLVF for all pools, set them later + for i = 0, 63 do + self.r.RTTDQSEL(i) + self.r.RTTDT1C(0x00) + end + + -- disable TC arbitrations, enable packet buffer free space monitor + self.r.RTTDCS:clr(bits { TDPAC=0, TDRM=4, BPBFSM=23 }) + self.r.RTTDCS:set(bits { VMPAC=1, BDPM=22 }) + self.r.RTTPCS:clr(bits { TPPAC=5, TPRM=8 }) + -- set RTTPCS.ARBD + self.r.RTTPCS:bits(22, 10, 0x244) + self.r.RTRPCS:clr(bits { RAC=2, RRM=1 }) + + -- must be cleared after MTQC configuration (7.2.1.2.1) + self.r.RTTDCS:clr(bits { ARBDIS=6 }) +end + +-- In VMDq mode, selects an available pool if one isn't provided by the user. +-- +-- This method runs before registers are loaded, because the rxq/txq registers +-- depend on the pool number prior to loading. As a result, we can't use the +-- lock_sw_sem() method to protect the critical section and use flock() instead. +local pooldir = "intel-mp-pools" +function Intel82599:select_pool() + if not self.vmdq then return end + + -- if the poolnum was set manually in the config, just use that + if not self.poolnum then + local available_pool + + -- We use some shared memory to track which pool numbers are claimed + -- using flock() to avoid conflicts. The contents of the memory doesn't + -- matter since we only care about the lock state. + for poolnum = 0, 63 do + local path = "/"..pooldir.."/"..self.pciaddress.."/"..poolnum + local ptr = shm.create(path, "uint8_t") + local poolfd = S.open(shm.root .. path, "creat, rdwr") + + if poolfd:flock("nb, ex") then + available_pool = poolnum + self.poolfd = poolfd + break + else + poolfd:close() + end + end + + assert(available_pool, "No free VMDq pools are available") + self.poolnum = available_pool + end + + -- Once we know the pool number, figure out txq and rxq numbers. This + -- needs to be done prior to loading registers. + -- + -- for VMDq, make rxq/txq relative to the pool number + assert(self.rxq >= 0 and self.rxq < 2, "rxqueue must be in 0..1") + self.rxq = self.rxq + 2 * self.poolnum + assert(self.txq >= 0 and self.txq < 2, "txqueue must be in 0..1") + self.txq = self.txq + 2 * self.poolnum + + -- max queue number is different in VMDq mode + self.max_q = 128 +end + +function Intel82599:enable_MAC_for_pool (mac_index) + self.r.MPSAR[2*mac_index + math.floor(self.poolnum/32)] + :set(bits{Ena=self.poolnum%32}) +end + +function Intel82599:unset_MAC () + local msk = bits { Ena=self.poolnum%32 } + for mac_index = 0, self.max_mac_addr do + self.r.MPSAR[2*mac_index + math.floor(self.poolnum/32)]:clr(msk) + end +end + +function Intel82599:set_tx_rate () + if not self.txq then return end + self.r.RTTDQSEL(self.poolnum or self.txq) + if self.rate_limit >= 10 then + -- line rate = 10,000 Mb/s + local factor = 10000 / tonumber(self.rate_limit) + -- 10.14 bits + factor = bit.band(math.floor(factor*2^14+0.5), 2^24-1) + self.r.RTTBCNRC(bits({RS_ENA=31}, factor)) + else + self.r.RTTBCNRC(0x00) + end + self.r.RTTDT1C(bit.band(math.floor(self.priority * 0x80), 0x3FF)) +end + +function Intel82599:unset_tx_rate () + self.rate_limit = 0 + self.priority = 0 + self:set_tx_rate() +end + +-- return rxstats for the counter assigned to this queue +-- the data has to be read from the shm frame since the main instance +-- is in control of the counter registers (and clears them on read) +function Intel82599:get_rxstats () + assert(self.rxcounter and self.rxq, "cannot retrieve rxstats") + local frame = shm.open_frame("pci/"..self.pciaddress) + local rxc = self.rxcounter + return { + counter_id = rxc, + packets = counter.read(frame["q"..rxc.."_rxpackets"]), + dropped = counter.read(frame["q"..rxc.."_rxdrops"]), + bytes = counter.read(frame["q"..rxc.."_rxbytes"]) + } +end + +function Intel82599:get_txstats () + assert(self.txcounter and self.txq, "cannot retrieve txstats") + local frame = shm.open_frame("pci/"..self.pciaddress) + local txc = self.txcounter + return { + counter_id = txc, + packets = counter.read(frame["q"..txc.."_txpackets"]), + bytes = counter.read(frame["q"..txc.."_txbytes"]) + } +end + +-- enable the given counter for this app's rx queue +function Intel82599:set_rxstats () + if not self.rxcounter or not self.rxq then return end + local counter = self.rxcounter + assert(counter>=0 and counter<16, "bad Rx counter") + self.r.RQSMR[math.floor(self.rxq/4)]:set(lshift(counter,8*(self.rxq%4))) +end + +-- enable the given counter for this app's tx queue +function Intel82599:set_txstats () + if not self.txcounter or not self.txq then return end + local counter = self.txcounter + assert(counter>=0 and counter<16, "bad Tx counter") + self.r.TQSM[math.floor(self.txq/4)]:set(lshift(counter,8*(self.txq%4))) +end + function Intel:debug (args) local args = args or {} local pfx = args.prefix or "DEBUG_" @@ -1025,8 +1612,13 @@ function Intel:debug (args) end if prnt then - for k,v in pairs(r) do - print(pfx..k,v) + local keys = {} + for k,_ in pairs(r) do + table.insert(keys, k) + end + table.sort(keys) + for _,k in ipairs(keys) do + print(pfx..k, r[k]) end end return r diff --git a/src/apps/intel_mp/selftest.sh b/src/apps/intel_mp/selftest.sh index 064de9b649..dc54fe2c33 100755 --- a/src/apps/intel_mp/selftest.sh +++ b/src/apps/intel_mp/selftest.sh @@ -1,11 +1,16 @@ #!/usr/bin/env bash cd $(dirname $0) -[ -z $SNABB_PCI_INTEL1G0 ] && exit $TEST_SKIPPED -[ -z $SNABB_PCI_INTEL1G1 ] && exit $TEST_SKIPPED -[ -z $SNABB_PCI_INTEL0 ] && exit $TEST_SKIPPED -[ -z $SNABB_PCI_INTEL1 ] && exit $TEST_SKIPPED +if [ $SNABB_PCI_INTEL1G0 ] && [ $SNABB_PCI_INTEL1G1 ]; then + TESTS1G=$(find . -executable | grep -e 'test_1g') +fi +if [ $SNABB_PCI_INTEL0 ] && [ $SNABB_PCI_INTEL1 ]; then + TESTS10G=$(find . -executable | grep -e 'test_10g') +fi +if [ -z "$TESTS1G" ] && [ -z "$TESTS10G" ]; then + exit $TEST_SKIPPED +fi FILTER=${1:-.*} -TESTS=$(find . -executable | grep -e 'test[0-9]' -e 'test_' | grep -e "$FILTER" | sort) +TESTS=$(echo "$TESTS1G" "$TESTS10G" | grep -e "$FILTER" | sort) ESTATUS=0 export SNABB_RECV_DEBUG=true export SNABB_RECV_MASTER_STATS=true @@ -19,9 +24,9 @@ for i in $TESTS; do echo "PASSED: $i" else for res in `ls results.*`; do - echo $res; - cat $res - echo + echo $res; + cat $res + echo done echo "FAILED: $i" ESTATUS=-1 diff --git a/src/apps/intel_mp/source-vlan.pcap b/src/apps/intel_mp/source-vlan.pcap new file mode 100644 index 0000000000..a4134e851b Binary files /dev/null and b/src/apps/intel_mp/source-vlan.pcap differ diff --git a/src/apps/intel_mp/source2.pcap b/src/apps/intel_mp/source2.pcap new file mode 100644 index 0000000000..fe11518fb3 Binary files /dev/null and b/src/apps/intel_mp/source2.pcap differ diff --git a/src/apps/intel_mp/test_10g_1q_blast_vmdq.sh b/src/apps/intel_mp/test_10g_1q_blast_vmdq.sh new file mode 100755 index 0000000000..176b253ac8 --- /dev/null +++ b/src/apps/intel_mp/test_10g_1q_blast_vmdq.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +SNABB_SEND_BLAST=true ./testsend.snabb $SNABB_PCI_INTEL1 0 source.pcap & +BLAST=$! + +SNABB_RECV_SPINUP=2 SNABB_RECV_DURATION=5 ./testvmdqrecv.snabb $SNABB_PCI_INTEL0 "90:72:82:78:c9:7a" nil 0 0 > results.0 + +kill -9 $BLAST +test `cat results.0 | grep "^RXDGPC" | awk '{print $2}'` -gt 10000 +exit $? diff --git a/src/apps/intel_mp/test_10g_2q_blast_vlan.sh b/src/apps/intel_mp/test_10g_2q_blast_vlan.sh new file mode 100755 index 0000000000..45ff7e891c --- /dev/null +++ b/src/apps/intel_mp/test_10g_2q_blast_vlan.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# +# Test VMDq mode with two pools with different VLANs + +SNABB_SEND_BLAST=true ./testsend.snabb $SNABB_PCI_INTEL1 0 source-vlan.pcap & +BLAST=$! + +SNABB_RECV_SPINUP=2 SNABB_RECV_DURATION=5 ./testvmdqrecv.snabb $SNABB_PCI_INTEL0 "90:72:82:78:c9:7a" 1 0 0 > results.0 & +PID1=$! +SNABB_RECV_SPINUP=2 SNABB_RECV_DURATION=5 ./testvmdqrecv.snabb $SNABB_PCI_INTEL0 "90:72:82:78:c9:7a" 2 1 0 > results.1 + +wait $PID1 +kill -9 $BLAST +[[ `cat results.* | grep "^RXDGPC" | awk '{print $2}'` -gt 10000 ]] &&\ +# both queues should see packets +[[ `cat results.0 | grep -m 1 fpb | awk '{print $9}'` -gt 0 ]] &&\ +[[ `cat results.1 | grep -m 1 fpb | awk '{print $9}'` -gt 0 ]] + +exit $? diff --git a/src/apps/intel_mp/test_10g_2q_blast_vmdq.sh b/src/apps/intel_mp/test_10g_2q_blast_vmdq.sh new file mode 100755 index 0000000000..f15f88886d --- /dev/null +++ b/src/apps/intel_mp/test_10g_2q_blast_vmdq.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# +# Test VMDq mode with two pools with different MACs + +SNABB_SEND_BLAST=true ./testsend.snabb $SNABB_PCI_INTEL1 0 source2.pcap & +BLAST=$! + +SNABB_RECV_SPINUP=2 SNABB_RECV_DURATION=5 ./testvmdqrecv.snabb $SNABB_PCI_INTEL0 "90:72:82:78:c9:7a" nil 0 0 > results.0 & +PID1=$! +SNABB_RECV_SPINUP=2 SNABB_RECV_DURATION=5 ./testvmdqrecv.snabb $SNABB_PCI_INTEL0 "12:34:56:78:9a:bc" nil 63 1 > results.1 + +wait $PID1 +kill -9 $BLAST + +# both queues should see packets +[[ `cat results.* | grep "^RXDGPC" | awk '{print $2}'` -gt 10000 ]] &&\ +[[ `cat results.0 | grep -m 1 fpb | awk '{print $9}'` -gt 0 ]] &&\ +[[ `cat results.1 | grep -m 1 fpb | awk '{print $9}'` -gt 0 ]] + +exit $? diff --git a/src/apps/intel_mp/test_10g_2q_blast_vmdq_auto.sh b/src/apps/intel_mp/test_10g_2q_blast_vmdq_auto.sh new file mode 100755 index 0000000000..11b17bd1a8 --- /dev/null +++ b/src/apps/intel_mp/test_10g_2q_blast_vmdq_auto.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# +# Test VMDq with automatic pool selection + +SNABB_SEND_BLAST=true ./testsend.snabb $SNABB_PCI_INTEL1 0 source2.pcap & +BLAST=$! + +SNABB_RECV_SPINUP=2 SNABB_RECV_DURATION=5 ./testvmdqrecv.snabb $SNABB_PCI_INTEL0 "90:72:82:78:c9:7a" nil nil 0 > results.0 & +PID1=$! +SNABB_RECV_SPINUP=2 SNABB_RECV_DURATION=5 ./testvmdqrecv.snabb $SNABB_PCI_INTEL0 "12:34:56:78:9a:bc" nil nil 1 > results.1 + +wait $PID1 +kill -9 $BLAST + +# both queues should see packets +[[ `cat results.* | grep "^RXDGPC" | awk '{print $2}'` -gt 10000 ]] &&\ +[[ `cat results.0 | grep -m 1 fpb | awk '{print $9}'` -gt 0 ]] &&\ +[[ `cat results.1 | grep -m 1 fpb | awk '{print $9}'` -gt 0 ]] + +exit $? diff --git a/src/apps/intel_mp/test_10g_come_and_go.sh b/src/apps/intel_mp/test_10g_come_and_go.sh index 9c538f1516..ce1a071945 100755 --- a/src/apps/intel_mp/test_10g_come_and_go.sh +++ b/src/apps/intel_mp/test_10g_come_and_go.sh @@ -2,12 +2,14 @@ SNABB_SEND_BLAST=true ./testsend.snabb $SNABB_PCI_INTEL1 0 source.pcap & BLAST=$! -SNABB_RECV_SPINUP=2 SNABB_RECV_DURATION=5 ./testrecv.snabb $SNABB_PCI_INTEL0 0 > results.0 & +SNABB_RECV_SPINUP=2 SNABB_RECV_DURATION=10 ./testrecv.snabb $SNABB_PCI_INTEL0 0 > results.0 & +RECV=$! sleep 1 export SNABB_RECV_DURATION=1 for i in {1..7}; do ./testrecv.snabb $SNABB_PCI_INTEL0 1; done > results.1 sleep 1 kill -9 $BLAST +wait $RECV test `cat results.* | grep "^RXDGPC" | awk '{print $2}'` -gt 10000 exit $? diff --git a/src/apps/intel_mp/test_10g_rate_limit.snabb b/src/apps/intel_mp/test_10g_rate_limit.snabb new file mode 100755 index 0000000000..412128781f --- /dev/null +++ b/src/apps/intel_mp/test_10g_rate_limit.snabb @@ -0,0 +1,43 @@ +#!../../snabb snsh + +-- Snabb test script for testing Tx rate limits + +local basic_apps = require("apps.basic.basic_apps") +local intel = require("apps.intel_mp.intel_mp") +local lib = require("core.lib") + +local pciaddr0 = lib.getenv("SNABB_PCI_INTEL0") +local pciaddr1 = lib.getenv("SNABB_PCI_INTEL1") + +local c = config.new() + +-- send packets on nic0 +config.app(c, "nic0", intel.driver, + { pciaddr = pciaddr0, + txq = 0, + -- minimum possible rate of 10Mbps + rate_limit = 10, + wait_for_link = true }) + +-- receive nic +config.app(c, "nic1", intel.driver, + { pciaddr = pciaddr1, + rxq = 0, + rxcounter = 1, + wait_for_link = true }) + +-- send 1KB packets repeatedly +config.app(c, "source", basic_apps.Source, 1024) +config.app(c, 'sink', basic_apps.Sink) + +config.link(c, "source.output -> nic0.input") +config.link(c, "nic1.output -> sink.input") + +engine.configure(c) +engine.main({ duration = 1 }) + +-- check that the stats show within 10% of 10Mb +local rxbytes = engine.app_table.nic1:get_rxstats().bytes +local target = 2^17 * 10 +assert(rxbytes > target * 0.90 and rxbytes < target * 1.10, + "expected about 1310720 bytes (10Mb), got " .. tonumber(rxbytes) .. " bytes") diff --git a/src/apps/intel_mp/test_10g_rss_tab.snabb b/src/apps/intel_mp/test_10g_rss_tab.snabb index a5f11d06f2..8f08223044 100755 --- a/src/apps/intel_mp/test_10g_rss_tab.snabb +++ b/src/apps/intel_mp/test_10g_rss_tab.snabb @@ -2,19 +2,23 @@ local intel = require("apps.intel_mp.intel_mp") local pci0 = os.getenv("SNABB_PCI_INTEL0") local pci1 = os.getenv("SNABB_PCI_INTEL1") -local nic = intel.Intel:new({ pciaddr = pci0 }) +local parse = require("core.lib").parse +local function new_intel (arg) + return intel.Intel:new(parse(arg, intel.Intel.config)) +end +local nic = new_intel({ pciaddr = pci0 }) local tab = nic:rss_tab() assert(#tab == 0) assert(tab[0]) -local nic0 = intel.Intel:new({pciaddr = pci0, rxq = 0}) -local nic1 = intel.Intel:new({pciaddr = pci0, rxq = 1}) +local nic0 = new_intel({pciaddr = pci0, rxq = 0}) +local nic1 = new_intel({pciaddr = pci0, rxq = 1}) tab = nic:rss_tab() assert(#tab == 1) assert(tab[0]) assert(tab[1]) -local nic2 = intel.Intel:new({pciaddr = pci0, rxq = 2}) -local nic3 = intel.Intel:new({pciaddr = pci0, rxq = 3}) +local nic2 = new_intel({pciaddr = pci0, rxq = 2}) +local nic3 = new_intel({pciaddr = pci0, rxq = 3}) tab = nic:rss_tab() assert(#tab == 3) assert(tab[2]) diff --git a/src/apps/intel_mp/test_10g_rxq_disable.snabb b/src/apps/intel_mp/test_10g_rxq_disable.snabb new file mode 100755 index 0000000000..967dfb89d5 --- /dev/null +++ b/src/apps/intel_mp/test_10g_rxq_disable.snabb @@ -0,0 +1,48 @@ +#!../../snabb snsh + +-- Test to make sure rx/tx queues can be explicitly disabled + +local basic_apps = require("apps.basic.basic_apps") +local intel = require("apps.intel_mp.intel_mp") +local lib = require("core.lib") + +local pciaddr0 = lib.getenv("SNABB_PCI_INTEL0") +local pciaddr1 = lib.getenv("SNABB_PCI_INTEL1") + +local c = config.new() + +config.app(c, "n0", intel.Intel, + { pciaddr = pciaddr0, + rxcounter = 1, + rxq = 0, txq = 0, + wait_for_link = true }) + +config.app(c, "n1", intel.Intel, + { pciaddr = pciaddr1, + rxq = "off", + txq = "off", + wait_for_link = true }) + +config.app(c, "source0", basic_apps.Source) +config.app(c, "source1", basic_apps.Source) + +config.app(c, "sink", basic_apps.Sink) + +config.link(c, "source0.output -> n0.input") +config.link(c, "source1.output -> n1.input") +config.link(c, "n0.output -> sink.input0") +config.link(c, "n1.output -> sink.input1") + +engine.configure(c) +engine.main({ duration = 1 }) + +-- no packets should get queued in either NIC +local n0_stats = engine.app_table.n0:get_rxstats() +assert(n0_stats.bytes == 0, "n0 received packets") + +-- can't call get_rxstats on disabled rxq so check RXDGPC +assert(engine.app_table.n1:rxdmapackets() == 0, + "n1 received packets") +-- also make sure packets *are* going to n1 though not to a queue +assert(engine.app_table.n1:rxbytes() > 0, + "n1 didn't see any traffic") diff --git a/src/apps/intel_mp/test_10g_sw_sem.snabb b/src/apps/intel_mp/test_10g_sw_sem.snabb index 657a938772..d603fa45ae 100755 --- a/src/apps/intel_mp/test_10g_sw_sem.snabb +++ b/src/apps/intel_mp/test_10g_sw_sem.snabb @@ -2,7 +2,11 @@ local intel = require("apps.intel_mp.intel_mp") local pci0 = os.getenv("SNABB_PCI_INTEL0") local pci1 = os.getenv("SNABB_PCI_INTEL1") -local nic = intel.Intel:new({pciaddr = pci0}) +local parse = require("core.lib").parse +local function new_intel (arg) + return intel.Intel:new(parse(arg, intel.Intel.config)) +end +local nic = new_intel({pciaddr = pci0, rxq = "off", txq = "off"}) nic:unlock_sw_sem() nic:lock_sw_sem() diff --git a/src/apps/intel_mp/test_10g_txq_stop.snabb b/src/apps/intel_mp/test_10g_txq_stop.snabb new file mode 100755 index 0000000000..1713bd49ed --- /dev/null +++ b/src/apps/intel_mp/test_10g_txq_stop.snabb @@ -0,0 +1,29 @@ +#!../../snabb snsh + +-- Test to make sure the app shuts down propertly on reconfiguration + +local basic_apps = require("apps.basic.basic_apps") +local intel = require("apps.intel_mp.intel_mp") +local lib = require("core.lib") + +local pciaddr0 = lib.getenv("SNABB_PCI_INTEL0") +local pciaddr1 = lib.getenv("SNABB_PCI_INTEL1") + +local c = config.new() + +config.app(c, "n1", intel.Intel, + { pciaddr = pciaddr1, + rxq = 0, + txq = 0, + wait_for_link = true }) +config.app(c, "source", basic_apps.Source) +config.app(c, "sink", basic_apps.Sink) +config.link(c, "source.output -> n1.input") +config.link(c, "n1.output -> sink.input") + +engine.configure(c) +engine.main({ duration = 1 }) + +local c2 = config.new() +engine.configure(c2) +engine.main({ duration = 1 }) diff --git a/src/apps/intel_mp/test_10g_vlan.sh b/src/apps/intel_mp/test_10g_vlan.sh new file mode 100755 index 0000000000..64048ea47c --- /dev/null +++ b/src/apps/intel_mp/test_10g_vlan.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# +# Test VMDq mode with vlan tagging + +./testvlan.snabb $SNABB_PCI_INTEL0 $SNABB_PCI_INTEL1 "90:72:82:78:c9:7a" source-vlan.pcap diff --git a/src/apps/intel_mp/test_10g_vmdq_mirror.snabb b/src/apps/intel_mp/test_10g_vmdq_mirror.snabb new file mode 100755 index 0000000000..8d02ec33b9 --- /dev/null +++ b/src/apps/intel_mp/test_10g_vmdq_mirror.snabb @@ -0,0 +1,75 @@ +#!../../snabb snsh + +-- Snabb test script for mirroring rules in VMDq mode +-- +-- Also tests rxcounters for consistency with link counts + +local basic_apps = require("apps.basic.basic_apps") +local intel = require("apps.intel_mp.intel_mp") +local pcap = require("apps.pcap.pcap") +local lib = require("core.lib") + +local pciaddr0 = lib.getenv("SNABB_PCI_INTEL0") +local pciaddr1 = lib.getenv("SNABB_PCI_INTEL1") + +local c = config.new() + +-- send packets on nic0 +config.app(c, "nic0", intel.Intel, + { pciaddr = pciaddr0, + txq = 0, + wait_for_link = true }) + +-- nic1 with three pools with several mirror configs +config.app(c, "nic1p0", intel.Intel, + { pciaddr = pciaddr1, + vmdq = true, + poolnum = 0, + macaddr = "90:72:82:78:c9:7a", + rxq = 0, + rxcounter = 1, + wait_for_link = true }) + +config.app(c, "nic1p1", intel.Intel, + { pciaddr = pciaddr1, + vmdq = true, + poolnum = 1, + mirror = { pool = { 0 } }, + macaddr = "12:34:56:78:9a:bc", + rxq = 0, + rxcounter = 2, + wait_for_link = true }) + +config.app(c, "nic1p2", intel.Intel, + { pciaddr = pciaddr1, + vmdq = true, + poolnum = 2, + mirror = { pool = true }, + macaddr = "aa:aa:aa:aa:aa:aa", + rxq = 0, + rxcounter = 3, + wait_for_link = true }) + +config.app(c, "pcap", pcap.PcapReader, "source2.pcap") +config.app(c, 'sink', basic_apps.Sink) + +config.link(c, "pcap.output -> nic0.input") +config.link(c, "nic1p0.output -> sink.input0") +config.link(c, "nic1p1.output -> sink.input1") +config.link(c, "nic1p2.output -> sink.input2") + +engine.configure(c) +engine.main({ duration = 1 }) + +assert(link.stats(engine.app_table.sink.input.input0).rxpackets == 51, + "wrong number of packets received on pool 0") +assert(engine.app_table.nic1p0:get_rxstats().packets == 51, + "expected get_rxstats and link stats to agree") +assert(link.stats(engine.app_table.sink.input.input1).rxpackets == 102, + "wrong number of packets received on pool 1") +assert(engine.app_table.nic1p1:get_rxstats().packets == 102, + "expected get_rxstats and link stats to agree") +assert(link.stats(engine.app_table.sink.input.input2).rxpackets == 102, + "wrong number of packets received on pool 2") +assert(engine.app_table.nic1p2:get_rxstats().packets == 102, + "expected get_rxstats and link stats to agree") diff --git a/src/apps/intel_mp/test_10g_vmdq_pool_sel.snabb b/src/apps/intel_mp/test_10g_vmdq_pool_sel.snabb new file mode 100755 index 0000000000..c43569b105 --- /dev/null +++ b/src/apps/intel_mp/test_10g_vmdq_pool_sel.snabb @@ -0,0 +1,69 @@ +#!../../snabb snsh + +-- Snabb test script for checking that pool selection is working +-- as expected across app shutdowns + +local basic_apps = require("apps.basic.basic_apps") +local intel = require("apps.intel_mp.intel_mp") +local pcap = require("apps.pcap.pcap") +local lib = require("core.lib") + +local pciaddr0 = lib.getenv("SNABB_PCI_INTEL0") +local pciaddr1 = lib.getenv("SNABB_PCI_INTEL1") + +local c = config.new() + +-- first add two apps in order & observe poolnums +config.app(c, "nicp0", intel.Intel, + { pciaddr = pciaddr1, + vmdq = true, + rxq = 0, txq = 0, + macaddr = "90:72:82:78:c9:7a" }) +config.app(c, "source", basic_apps.Source) + +config.link(c, "source.out0 -> nicp0.input") + +engine.configure(c) +engine.main({ duration = 0.1 }) + +assert(engine.app_table.nicp0.poolnum == 0, "wrong poolnum for nicp0") + +config.app(c, "nicp1", intel.Intel, + { pciaddr = pciaddr1, + vmdq = true, + rxq = 0, txq = 0, + macaddr = "90:72:82:78:c9:7b" }) +config.link(c, "source.out1 -> nicp1.input") + +engine.configure(c) +engine.main({ duration = 0.1 }) + +assert(engine.app_table.nicp1.poolnum == 1, "wrong poolnum for nicp1") + +-- now try removing the first app, then add a new one to use pool 0 +local c = config.new() + +config.app(c, "nicp1", intel.Intel, + { pciaddr = pciaddr1, + vmdq = true, + rxq = 0, txq = 0, + macaddr = "90:72:82:78:c9:7b" }) +config.app(c, "source", basic_apps.Source) +config.link(c, "source.out1 -> nicp1.input") + +engine.configure(c) +engine.main({ duration = 0.1 }) + +config.app(c, "nicp2", intel.Intel, + { pciaddr = pciaddr1, + vmdq = true, + rxq = 0, txq = 0, + macaddr = "90:72:82:78:c9:7b" }) +config.link(c, "source.out2 -> nicp2.input") + +engine.configure(c) +engine.main({ duration = 0.1 }) + +assert(engine.app_table.nicp1.poolnum == 1, "wrong poolnum for nicp1") +-- pool 0 should be freed by nicp0 being stopped +assert(engine.app_table.nicp2.poolnum == 0, "wrong poolnum for nicp2") diff --git a/src/apps/intel_mp/test_10g_vmdq_race.snabb b/src/apps/intel_mp/test_10g_vmdq_race.snabb new file mode 100755 index 0000000000..32cc52e371 --- /dev/null +++ b/src/apps/intel_mp/test_10g_vmdq_race.snabb @@ -0,0 +1,68 @@ +#!../../snabb snsh + +-- Snabb test script that tests against race conditions in setting +-- VMDq parameters like MAC and VLAN + +local C = require("ffi").C +local intel = require("apps.intel_mp.intel_mp") +local lib = require("core.lib") +local worker = require("core.worker") + +local pciaddr0 = lib.getenv("SNABB_PCI_INTEL0") + +-- launch two worker processes each using VMDq with MAC addresses +-- and different VLAN tags and ensure that the chosen indices for +-- MAC & VLAN do not overlap. +-- +-- It's difficult for this test to fail unless delays are introduced +-- to deliberately trigger race conditions in the driver code (and +-- the locks disabled). +worker.start("worker0", [[ + local intel = require("apps.intel_mp.intel_mp") + local lib = require("core.lib") + local pciaddr0 = lib.getenv("SNABB_PCI_INTEL0") + local c = config.new() + config.app(c, "nic0", intel.Intel, + { pciaddr = pciaddr0, + vmdq = true, + poolnum = 0, + rxq = 0, txq = 0, + vlan = 0, + macaddr = "00:11:22:33:44:55" }) + engine.configure(c) + engine.main({ duration = 1 }) + assert(engine.app_table.nic0.r.RAH[1]:bits(31, 1) == 1) + assert(engine.app_table.nic0.r.PFVLVF[0]:bits(31, 1) == 1) +]]) + +worker.start("worker1", [[ + local intel = require("apps.intel_mp.intel_mp") + local lib = require("core.lib") + local pciaddr0 = lib.getenv("SNABB_PCI_INTEL0") + local c = config.new() + config.app(c, "nic1", intel.Intel, + { pciaddr = pciaddr0, + vmdq = true, + poolnum = 1, + rxq = 0, txq = 0, + vlan = 1, + macaddr = "55:44:33:22:11:00" }) + engine.configure(c) + engine.main({ duration = 1 }) + assert(engine.app_table.nic1.r.RAH[2]:bits(31, 1) == 1) + assert(engine.app_table.nic1.r.PFVLVF[1]:bits(31, 1) == 1) +]]) + +-- loop until all workers are done +while true do + local live = false + for w, s in pairs(worker.status()) do + live = live or s.alive + end + + if live then + C.sleep(0.1) + else + break + end +end diff --git a/src/apps/intel_mp/test_10g_vmdq_reconfig_mac.snabb b/src/apps/intel_mp/test_10g_vmdq_reconfig_mac.snabb new file mode 100755 index 0000000000..72281224a3 --- /dev/null +++ b/src/apps/intel_mp/test_10g_vmdq_reconfig_mac.snabb @@ -0,0 +1,80 @@ +#!../../snabb snsh + +-- Snabb test script for checking that the MAC & VLAN indices +-- are selected correctly after an intel_mp instance is shut +-- down and restarted with a different config. +-- +-- This test exists to test the bug that was fixed in commit +-- 721f10bb0c3e076f79c54086fefd554851ac9679 + +local basic_apps = require("apps.basic.basic_apps") +local intel = require("apps.intel_mp.intel_mp") +local pcap = require("apps.pcap.pcap") +local lib = require("core.lib") + +local pciaddr0 = lib.getenv("SNABB_PCI_INTEL0") + +local c = config.new() + +-- first add two apps in order with different MACs and VLANs +config.app(c, "nicp0", intel.Intel, + { pciaddr = pciaddr0, + vmdq = true, + poolnum = 0, + rxq = 0, txq = 0, + vlan = 42, + macaddr = "00:11:22:33:44:55" }) +config.app(c, "source", basic_apps.Source) + +config.link(c, "source.out0 -> nicp0.input") + +engine.configure(c) +engine.main({ duration = 0.1 }) + +config.app(c, "nicp1", intel.Intel, + { pciaddr = pciaddr0, + vmdq = true, + poolnum = 1, + rxq = 0, txq = 0, + vlan = 43, + macaddr = "55:44:33:22:11:00" }) +config.link(c, "source.out1 -> nicp1.input") + +engine.configure(c) +engine.main({ duration = 0.1 }) + +-- then shut down the first one and start a new one on the same +-- pool as nicp1 +local c = config.new() + +config.app(c, "nicp1", intel.Intel, + { pciaddr = pciaddr0, + vmdq = true, + poolnum = 1, + rxq = 0, txq = 0, + vlan = 43, + macaddr = "55:44:33:22:11:00" }) +config.app(c, "source", basic_apps.Source) +config.link(c, "source.out1 -> nicp1.input") + +config.app(c, "nicp2", intel.Intel, + { pciaddr = pciaddr0, + vmdq = true, + poolnum = 1, + rxq = 1, txq = 1, + vlan = 43, + macaddr = "55:44:33:22:11:00" }) +config.link(c, "source.out2 -> nicp2.input") + +engine.configure(c) +engine.main({ duration = 0.1 }) + +-- MAC addresses are never disabled by intel_mp (or intel10g) +-- so both stay enabled. +-- TODO: this is issue #1205 (https://github.com/snabbco/snabb/issues/1205) +-- and these checks may need to change when that is resolved +assert(engine.app_table.nicp1.r.RAH[1]:bits(31, 1) == 1) +assert(engine.app_table.nicp2.r.RAH[2]:bits(31, 1) == 1) +-- only the second VLAN register should be enabled +assert(engine.app_table.nicp1.r.PFVLVF[0]:bits(31, 1) == 0) +assert(engine.app_table.nicp2.r.PFVLVF[1]:bits(31, 1) == 1) diff --git a/src/apps/intel_mp/test_10g_vmdq_tx.sh b/src/apps/intel_mp/test_10g_vmdq_tx.sh new file mode 100755 index 0000000000..7a98961316 --- /dev/null +++ b/src/apps/intel_mp/test_10g_vmdq_tx.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# +# Test packet transmit for VMDq mode + +./testvmdqtx.snabb $SNABB_PCI_INTEL0 $SNABB_PCI_INTEL1 "50:46:5d:74:1d:f9" source.pcap diff --git a/src/apps/intel_mp/test_1g_rss_tab.snabb b/src/apps/intel_mp/test_1g_rss_tab.snabb index 0fb11b7ae4..9a9ca3814c 100755 --- a/src/apps/intel_mp/test_1g_rss_tab.snabb +++ b/src/apps/intel_mp/test_1g_rss_tab.snabb @@ -2,19 +2,23 @@ local intel = require("apps.intel_mp.intel_mp") local pci0 = os.getenv("SNABB_PCI_INTEL1G0") local pci1 = os.getenv("SNABB_PCI_INTEL1G1") -local nic = intel.Intel:new({ pciaddr = pci0 }) +local parse = require("core.lib").parse +local function new_intel (arg) + return intel.Intel:new(parse(arg, intel.Intel.config)) +end +local nic = new_intel({ pciaddr = pci0 }) local tab = nic:rss_tab() assert(#tab == 0) assert(tab[0]) -local nic0 = intel.Intel:new({pciaddr = pci0, rxq = 0}) -local nic1 = intel.Intel:new({pciaddr = pci0, rxq = 1}) +local nic0 = new_intel({pciaddr = pci0, rxq = 0}) +local nic1 = new_intel({pciaddr = pci0, rxq = 1}) tab = nic:rss_tab() assert(#tab == 1) assert(tab[0]) assert(tab[1]) -local nic2 = intel.Intel:new({pciaddr = pci0, rxq = 2}) -local nic3 = intel.Intel:new({pciaddr = pci0, rxq = 3}) +local nic2 = new_intel({pciaddr = pci0, rxq = 2}) +local nic3 = new_intel({pciaddr = pci0, rxq = 3}) tab = nic:rss_tab() assert(#tab == 3) assert(tab[2]) diff --git a/src/apps/intel_mp/test_1g_sw_sem.snabb b/src/apps/intel_mp/test_1g_sw_sem.snabb index fe5558e256..3fbb928c47 100755 --- a/src/apps/intel_mp/test_1g_sw_sem.snabb +++ b/src/apps/intel_mp/test_1g_sw_sem.snabb @@ -2,7 +2,11 @@ local intel = require("apps.intel_mp.intel_mp") local pci0 = os.getenv("SNABB_PCI_INTEL1G0") local pci1 = os.getenv("SNABB_PCI_INTEL1G1") -local nic = intel.Intel:new({pciaddr = pci0}) +local parse = require("core.lib").parse +local function new_intel (arg) + return intel.Intel:new(parse(arg, intel.Intel.config)) +end +local nic = new_intel({pciaddr = pci0}) nic:unlock_sw_sem() nic:lock_sw_sem() diff --git a/src/apps/intel_mp/testrecv.lua b/src/apps/intel_mp/testrecv.lua new file mode 100644 index 0000000000..4c391dc412 --- /dev/null +++ b/src/apps/intel_mp/testrecv.lua @@ -0,0 +1,90 @@ +-- Helper module for testing intel_mp driver receive + +module(..., package.seeall) + +local intel = require("apps.intel_mp.intel_mp") +local basic = require("apps.basic.basic_apps") +local ffi = require("ffi") +local C = ffi.C + +function test(pciaddr, qno, vmdq, poolno, macaddr, vlan) + local c = config.new() + if vmdq then + config.app(c, "nic", intel.Intel, + { pciaddr=pciaddr, + macaddr=macaddr, + vlan=vlan, + vmdq=true, + poolnum=poolno, + rxq = qno, + ndescriptors = 2048, + wait_for_link=true }) + else + config.app(c, "nic", intel.Intel, + { pciaddr=pciaddr, + rxq = qno, + ndescriptors = 2048, + wait_for_link=true }) + end + config.app(c, "sink", basic.Sink) + if os.getenv("SNABB_RECV_EXPENSIVE") then + local filter = require("apps.packet_filter.pcap_filter") + + local count = 10 + config.link(c, "nic.output -> filter0.input") + for i=0,count do + local n = tostring(i) + local s = "filter"..n + config.app(c, s, filter.PcapFilter, { filter = [[ not dst host 10.2.29.1 and not dst host 10.2.50.1 ]]}) + end + for i=1,count do + local m = tostring(i-1) + local n = tostring(i) + local s = "filter"..m..".output -> filter"..n..".input" + config.link(c, s) + end + config.app(c, "sane", filter.PcapFilter, { filter = [[ src host 172.16.172.3 and dst net 1.2.0.0/16 and ip proto 0 ]] }) + config.link(c, "filter"..tostring(count)..".output -> sane.input") + config.link(c, "sane.output -> sink.input") + else + config.link(c, "nic.output -> sink.input") + end + + engine.configure(c) + local spinup = os.getenv("SNABB_RECV_SPINUP") + if spinup then + engine.main({duration = spinup}) + end + + local counters = { + Intel82599 = { "GPRC", "RXDGPC" }, + Intel1g = { "GPRC", "RPTHC" } + } + + local duration = os.getenv("SNABB_RECV_DURATION") or 2 + local before = {} + local nic = engine.app_table.nic + local master = nic.master + + if master then + for _,v in pairs(counters[nic.driver]) do + before[v] = nic.r[v]() + end + end + + if os.getenv("SNABB_RECV_DEBUG") then + for _=1,duration do + engine.main({duration = 1}) + nic:debug() + end + else + engine.main({duration = duration}) + end + + if master then + for _,v in pairs(counters[nic.driver]) do + print(string.format("%s %d", v, tonumber(nic.r[v]() - before[v])/duration)) + end + end + main.exit(0) +end diff --git a/src/apps/intel_mp/testrecv.snabb b/src/apps/intel_mp/testrecv.snabb index 6b41b14aa8..acbfdd8ec1 100755 --- a/src/apps/intel_mp/testrecv.snabb +++ b/src/apps/intel_mp/testrecv.snabb @@ -1,74 +1,10 @@ #!../../snabb snsh + local args = main.parameters assert(#args == 2, "testrecv.snabb pciaddr qno") local pciaddr = table.remove(args, 1) local qno = tonumber(table.remove(args,1)) -local intel = require("apps.intel_mp.intel_mp") -local basic = require("apps.basic.basic_apps") -local ffi = require("ffi") -local C = ffi.C - -local c = config.new() -config.app(c, "nic", intel.Intel, { pciaddr=pciaddr, rxq = qno, ndescriptors = 2048, wait_for_link=true }) -config.app(c, "sink", basic.Sink) -if os.getenv("SNABB_RECV_EXPENSIVE") then - local filter = require("apps.packet_filter.pcap_filter") - - local count = 10 - config.link(c, "nic.output -> filter0.input") - for i=0,count do - local n = tostring(i) - local s = "filter"..n - config.app(c, s, filter.PcapFilter, { filter = [[ not dst host 10.2.29.1 and not dst host 10.2.50.1 ]]}) - end - for i=1,count do - local m = tostring(i-1) - local n = tostring(i) - local s = "filter"..m..".output -> filter"..n..".input" - config.link(c, s) - end - config.app(c, "sane", filter.PcapFilter, { filter = [[ src host 172.16.172.3 and dst net 1.2.0.0/16 and ip proto 0 ]] }) - config.link(c, "filter"..tostring(count)..".output -> sane.input") - config.link(c, "sane.output -> sink.input") -else - config.link(c, "nic.output -> sink.input") -end - -engine.configure(c) -local spinup = os.getenv("SNABB_RECV_SPINUP") -if spinup then - engine.main({duration = spinup}) -end - -local counters = { - Intel82599 = { "GPRC", "RXDGPC" }, - Intel1g = { "GPRC", "RPTHC" } -} - -local duration = os.getenv("SNABB_RECV_DURATION") or 2 -local before = {} -local nic = engine.app_table.nic -local master = nic.master - -if master then - for _,v in pairs(counters[nic.driver]) do - before[v] = nic.r[v]() - end -end - -if os.getenv("SNABB_RECV_DEBUG") then - for _=1,duration do - engine.main({duration = 1}) - nic:debug() - end -else - engine.main({duration = duration}) -end +local test = require("apps.intel_mp.testrecv").test -if master then - for _,v in pairs(counters[nic.driver]) do - print(string.format("%s %d", v, tonumber(nic.r[v]() - before[v])/duration)) - end -end -main.exit(0) +test(pciaddr, qno, false) diff --git a/src/apps/intel_mp/testup.snabb b/src/apps/intel_mp/testup.snabb index 2279a43729..b31ec6a2e5 100755 --- a/src/apps/intel_mp/testup.snabb +++ b/src/apps/intel_mp/testup.snabb @@ -7,6 +7,12 @@ local qno = tonumber(table.remove(args,1)) local intel = require("apps.intel_mp.intel_mp") local C = require("ffi").C -local nic = intel.Intel:new({ pciaddr=pciaddr, rxq = qno, ndescriptors = 2048, wait_for_link = true }) +local parse = require("core.lib").parse +local function new_intel (arg) + return intel.Intel:new(parse(arg, intel.Intel.config)) +end + +local nic = new_intel( + { pciaddr=pciaddr, rxq = qno, ndescriptors = 2048, wait_for_link = true }) print(nic:link_status()) main.exit(0) diff --git a/src/apps/intel_mp/testvlan.snabb b/src/apps/intel_mp/testvlan.snabb new file mode 100755 index 0000000000..c041e20279 --- /dev/null +++ b/src/apps/intel_mp/testvlan.snabb @@ -0,0 +1,57 @@ +#!../../snabb snsh + +-- Snabb test script for vlan tagging/insertion. This test is for checking +-- that vlan stripping is working. + +local args = main.parameters +assert(#args == 4, "testvlan.snabb pciaddr1 pciaddr2 macaddr pcapfile") +local pciaddr1 = table.remove(args, 1) +local pciaddr2 = table.remove(args, 1) +local macaddr = table.remove(args, 1) +local pcapfile = table.remove(args,1) + +local basic_apps = require("apps.basic.basic_apps") +local intel = require("apps.intel_mp.intel_mp") +local pcap = require("apps.pcap.pcap") +local filter = require("apps.packet_filter.pcap_filter") +local C = require("ffi").C + +local c = config.new() +config.app(c, "pcap", pcap.PcapReader, pcapfile) + +-- these are on the same core but that's ok, this is a correctness test +config.app(c, "nic1", intel.Intel, + {pciaddr=pciaddr1, + txq = 0, + ndescriptors=2048, + wait_for_link = true}) +config.app(c, "nic2", intel.Intel, + {pciaddr=pciaddr2, + vmdq = true, + macaddr = macaddr, + vlan = 1, + poolnum = 0, + rxq = 0, + ndescriptors=2048, + wait_for_link = true}) + +-- use pflua to filter based on presence of vlan tag +config.app(c, "filter1", filter.PcapFilter, { filter = [[ ether[12:2] == 0x8100 ]] }) +config.app(c, "filter2", filter.PcapFilter, { filter = [[ ether[12:2] != 0x8100 ]] }) +config.app(c, "tee", basic_apps.Tee) +config.app(c, 'sink', basic_apps.Sink) + +config.link(c, "pcap.output -> nic1.input") +config.link(c, "nic2.output -> tee.input") +config.link(c, "tee.output1 -> filter1.input") +config.link(c, "tee.output2 -> filter2.input") +config.link(c, "filter1.output -> sink.in1") +config.link(c, "filter2.output -> sink.in2") + +engine.configure(c) +engine.main({ duration = 1 }) + +assert(link.stats(engine.app_table.sink.input.in1).rxpackets == 0, + "expected zero vlan tagged packets (after stripping)") +assert(link.stats(engine.app_table.sink.input.in2).rxpackets > 0, + "expected some non-vlan tagged packets (after stripping)") diff --git a/src/apps/intel_mp/testvmdqrecv.snabb b/src/apps/intel_mp/testvmdqrecv.snabb new file mode 100755 index 0000000000..4ca824e301 --- /dev/null +++ b/src/apps/intel_mp/testvmdqrecv.snabb @@ -0,0 +1,12 @@ +#!../../snabb snsh +local args = main.parameters +assert(#args == 5, "testvmdqrecv.snabb pciaddr macaddr vlan poolno qno") +local pciaddr = table.remove(args, 1) +local macaddr = table.remove(args, 1) +local vlan = load("return " .. table.remove(args, 1))() +local poolno = load("return " .. table.remove(args, 1))() +local qno = tonumber(table.remove(args,1)) + +local test = require("apps.intel_mp.testrecv").test + +test(pciaddr, qno, true, poolno, macaddr, vlan) diff --git a/src/apps/intel_mp/testvmdqtx.snabb b/src/apps/intel_mp/testvmdqtx.snabb new file mode 100755 index 0000000000..94efcf7325 --- /dev/null +++ b/src/apps/intel_mp/testvmdqtx.snabb @@ -0,0 +1,61 @@ +#!../../snabb snsh + +-- Snabb test script for transmit in VMDq mode, also tests VLAN +-- tag insertion +-- +-- Also ensures that the Tx counters work + +local args = main.parameters +assert(#args == 4, "testvmdqtx.snabb pciaddr1 pciaddr2 macaddr pcapfile") +local pciaddr1 = table.remove(args, 1) +local pciaddr2 = table.remove(args, 1) +local macaddr = table.remove(args, 1) +local pcapfile = table.remove(args,1) + +local basic_apps = require("apps.basic.basic_apps") +local intel = require("apps.intel_mp.intel_mp") +local pcap = require("apps.pcap.pcap") +local filter = require("apps.packet_filter.pcap_filter") +local C = require("ffi").C + +local c = config.new() + +-- send packets on nic1 +config.app(c, "nic1", intel.Intel, + { pciaddr=pciaddr1, + vmdq = true, + macaddr = macaddr, + poolnum = 0, + txq = 0, + txcounter = 1, + vlan = 1, + ndescriptors=2048, + wait_for_link = true }) + +-- nic2 just receives packets so we can check them +config.app(c, "nic2", intel.Intel, + { pciaddr=pciaddr2, + rxq = 0, + ndescriptors=2048, + wait_for_link = true }) + +config.app(c, "pcap", pcap.PcapReader, pcapfile) +config.app(c, "filter", filter.PcapFilter, { filter = [[ ether[12:2] == 0x8100 ]] }) +config.app(c, 'sink', basic_apps.Sink) + +config.link(c, "pcap.output -> nic1.input") +config.link(c, "nic2.output -> filter.input") +config.link(c, "filter.output -> sink.input") + +engine.configure(c) +engine.main({ duration = 1 }) + +assert(link.stats(engine.app_table.nic1.input.input).rxpackets > 0, + "expected some packets from pcap to Tx NIC") +assert(link.stats(engine.app_table.filter.input.input).rxpackets > 0, + "expected some packets on the receiving NIC") +assert(link.stats(engine.app_table.filter.input.input).rxpackets == + engine.app_table.nic1:get_txstats().packets, + "expected txstats() to match up with link stats") +assert(link.stats(engine.app_table.sink.input.input).rxpackets > 0, + "expected VLAN tagged packets on the receiving NIC") diff --git a/src/doc/branches.md b/src/doc/branches.md index 6f55a94577..8aa22a89a2 100644 --- a/src/doc/branches.md +++ b/src/doc/branches.md @@ -166,7 +166,7 @@ The current state of each branch with respect to master is visible here: - See snabbwall.org for more info Maintainer: Collectively maintained by Snabbwall application developers. - Next hop: kbara-next + Next hop: wingo-next #### aarch64 diff --git a/src/program/wall/COPYRIGHT.md b/src/program/wall/COPYRIGHT.md new file mode 100644 index 0000000000..c9eadcd4da --- /dev/null +++ b/src/program/wall/COPYRIGHT.md @@ -0,0 +1,4 @@ +Copyright: 2017, Igalia and the Snabb project. +License: See COPYING. + +Snabbwall development has been kindly funded by NLnet Foundation (https://nlnet.nl/). diff --git a/src/program/wall/README b/src/program/wall/README index 71b300cfae..0fc8d43f76 100644 --- a/src/program/wall/README +++ b/src/program/wall/README @@ -5,6 +5,7 @@ Usage: Available subcommands: spy Analyze traffic and report statistics + filter Apply filtering rules to incoming packets. Use --help for per-command usage. Example: diff --git a/src/program/wall/common.lua b/src/program/wall/common.lua index 7439635dcb..fe92867451 100644 --- a/src/program/wall/common.lua +++ b/src/program/wall/common.lua @@ -1,3 +1,4 @@ +-- Use of this source code is governed by the Apache 2.0 license; see COPYING. module(..., package.seeall) -- This module provides some common definitions for snabbwall programs diff --git a/src/program/wall/filter/README b/src/program/wall/filter/README index 56054e39c5..fbd4f5ccd0 100644 --- a/src/program/wall/filter/README +++ b/src/program/wall/filter/README @@ -26,3 +26,11 @@ Options: -6, --ipv6 Set the IPv6 address of this firewall host -D, --duration Set the duration to run the program (in seconds). --cpu Pin to a particular CPU and appropriate NUMA node + +Example: + +# Reject all HTTP packets and accept all the test. +sudo ./snabb wall filter -e "{ HTTP = 'reject', default = 'accept' }" pcap v6-http.cap + +# Accept RTP packets which flow_count is equals or higher than 69 and drop otherwise. Drop non RTP packets. +sudo ./snabb wall filter -e "{ RTP = [[match { flow_count >= 69 => accept; otherwise => drop }]], default = 'drop' }" pcap rtp_example.pcap diff --git a/src/program/wall/filter/filter.lua b/src/program/wall/filter/filter.lua index c55fe4c2b3..8eba882e16 100644 --- a/src/program/wall/filter/filter.lua +++ b/src/program/wall/filter/filter.lua @@ -1,3 +1,4 @@ +-- Use of this source code is governed by the Apache 2.0 license; see COPYING. module(..., package.seeall) local fw = require("apps.wall.l7fw") diff --git a/src/program/wall/spy/spy.lua b/src/program/wall/spy/spy.lua index 442d4d04cd..c070ead9aa 100644 --- a/src/program/wall/spy/spy.lua +++ b/src/program/wall/spy/spy.lua @@ -1,3 +1,4 @@ +-- Use of this source code is governed by the Apache 2.0 license; see COPYING. module(..., package.seeall) local lib = require("core.lib") diff --git a/src/program/wall/tests/filter-pcaps.test b/src/program/wall/tests/filter-pcaps.test index be5b059ca2..1bb0ab3334 100755 --- a/src/program/wall/tests/filter-pcaps.test +++ b/src/program/wall/tests/filter-pcaps.test @@ -1,4 +1,10 @@ #! /usr/bin/env bash + +if [[ $EUID != 0 ]]; then + echo "This script must be run as root" 1>&2 + exit 1 +fi + set -e shopt -s nullglob @@ -13,20 +19,31 @@ readonly mac="01:23:45:67:89:ab" # run a test given the pcap file path, the no. of packets expected to # the output file, the no. packets for the reject file, and a firewall policy function test-filter { + local test_name=$1 n_accepted=$2 n_rejected=$3 n_dropped=$4 rule=$5 output=`mktemp` - echo "TEST $1" - "${mydir}/../../../snabb" wall filter -p -4 $ip4 -6 $ip6 -m $mac -o `mktemp` -r `mktemp` -e "$5" pcap "${datadir}/$1" > $output - if ! (grep "Accepted packets: $2" $output && - grep "Rejected packets: $3" $output && - grep "Dropped packets: $4" $output); then + echo "TEST $test_name" + "${mydir}/../../../snabb" wall filter -p -4 $ip4 -6 $ip6 -m $mac -o `mktemp` -r `mktemp` -e "$rule" pcap "${datadir}/$test_name" > $output + if ! (grep "Accepted packets: $n_accepted" $output && + grep "Rejected packets: $n_rejected" $output && + grep "Dropped packets: $n_dropped" $output); then echo "FAIL" - return 1 + result=1 + else + echo "SUCCESS" + result=0 fi - echo "SUCCESS" - return 0 + rm $output + return $result } +# Reject all DHCPv6 packets and drop all the rest. test-filter "dhcpv6.pcap" 0 6 4 "{ DHCPV6 = 'reject', default = 'drop' }" + +# Reject all HTTP packets and accept all the test. test-filter "v6-http.cap" 51 4 0 "{ HTTP = 'reject', default = 'accept' }" + +# Accept RTP packets which flow_count is equals or higher than 69 and drop otherwise. Drop non RTP packets. test-filter "rtp_example.pcap" 465 0 34 "{ RTP = [[match { flow_count >= 69 => accept; otherwise => drop }]], default = 'drop' }" + +# Reject RTP packets which flow_count is equals or higher than 69 and drop otherwise. Drop non RTP packets. test-filter "rtp_example.pcap" 0 465 34 "{ RTP = [[match { flow_count >= 69 => reject; otherwise => drop }]], default = 'drop' }" diff --git a/src/program/wall/wall.lua b/src/program/wall/wall.lua index bf672f9f4e..3bfd8f242a 100644 --- a/src/program/wall/wall.lua +++ b/src/program/wall/wall.lua @@ -1,3 +1,4 @@ +-- Use of this source code is governed by the Apache 2.0 license; see COPYING. module(..., package.seeall) local lib = require("core.lib")