From eb6d50cbbe36c4cb16b5f0155941155177e4121f Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Fri, 4 Nov 2016 14:32:02 +0100 Subject: [PATCH 01/14] core.config: add `configure' callback to apps. --- src/core/config.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/config.lua b/src/core/config.lua index 16492c1a1c..3c8fd53630 100644 --- a/src/core/config.lua +++ b/src/core/config.lua @@ -32,7 +32,11 @@ function app (config, name, class, arg) if status then arg = result else error("failed to configure '"..name.."': "..result) end end - config.apps[name] = { class = class, arg = arg} + if class.configure then + class:configure(config, name, arg) + else + config.apps[name] = { class = class, arg = arg} + end end -- API: Add a link to the configuration. From 3f61110f106124dad086ef0cab2f2243fd930e54 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 9 Nov 2016 14:59:44 +0100 Subject: [PATCH 02/14] apps.io: introduce macro apps io.IOControl and io.IO, as well as emu.Emu. --- src/apps/io/emu.lua | 109 ++++++++++++++++++++++ src/apps/io/io.lua | 164 ++++++++++++++++++++++++++++++++++ src/apps/socket/raw.lua | 1 + src/apps/tap/tap.lua | 1 + src/apps/vhost/vhost_user.lua | 1 + 5 files changed, 276 insertions(+) create mode 100644 src/apps/io/emu.lua create mode 100644 src/apps/io/io.lua diff --git a/src/apps/io/emu.lua b/src/apps/io/emu.lua new file mode 100644 index 0000000000..24684f6a21 --- /dev/null +++ b/src/apps/io/emu.lua @@ -0,0 +1,109 @@ +-- Use of this source code is governed by the Apache 2.0 license; see COPYING. + +module(..., package.seeall) +local FloodingBridge = require("apps.bridge.flooding").bridge +local ethernet = require("lib.protocol.ethernet") +local ipv6 = require("lib.protocol.ipv6") +local murmur = require("lib.hash.murmur") +local C = require("ffi").C + + +control = { + config = { + virtual = {}, + queues = {required=true} + } +} + +local bridge = {}; setmetatable(bridge, {__mode="k"}) -- Weak keys + +function control:configure (c, name, conf) + bridge[c] = name + local ports = {} + for _, queuespec in ipairs(conf.queues) do + if not queuespec.buckets or queuespec.buckets == 1 then + table.insert(ports, queuespec.id) + else + for bucket = 1, queuespec.buckets do + table.insert(ports, queuespec.id.."_"..bucket) + end + end + end + config.app(c, name, FloodingBridge, {ports=ports}) +end + + +driver = { + config = { + queue = {required=true}, + bucket = {default=1}, + buckets = {default=1}, + queueconf = {required=true} + } +} + +Emu = {config = driver.config} + +function driver:configure (c, name, conf) + config.app(c, name, Emu, conf) + local port + if conf.buckets == 1 then + port = conf.queue + else + port = conf.queue.."_"..conf.bucket + end + config.link(c, name..".trunk -> "..bridge[c].."."..port) + config.link(c, bridge[c].."."..port.." -> "..name..".trunk") +end + +function Emu:new (conf) + local o = {} + if conf.macaddr then + o.mac = ethernet:pton(conf.macaddr) + end + if conf.buckets > 1 then + o.murmur = murmur.MurmurHash3_x86_32:new() + o.buckets = conf.buckets + o.bucket = conf.bucket + end + return setmetatable(o, {__index=Emu}) +end + +local ADDRESS_SIZE = 6 +local SRC_OFFSET = ADDRESS_SIZE +local MIN_SIZE = ethernet:sizeof() + ipv6:sizeof() +local IP_SRC_DST_OFFSET = ethernet:sizeof() + 8 +local IP_SRC_DST_SIZE = 2*16 + +function Emu:hash (p) + return self.murmur:hash(p.data+IP_SRC_DST_OFFSET, IP_SRC_DST_SIZE, 0ULL) +end + +function Emu:push () + local mac, bucket, buckets = self.max, self.bucket, self.buckets + local l_in = assert(self.input.trunk, "No input link on trunk.") + local l_out = assert(self.output.tx, "No output link on tx.") + for i = 1, link.nreadable(l_in) do + local p = link.receive(l_in) + if p.length < MIN_SIZE + or (mac and C.memcmp(mac, p.data, ADDRESS_SIZE) ~= 0) + or (bucket and 1 + (self:hash(p) % buckets) ~= bucket) + then + packet.free(p) + else + link.transmit(l_out, p) + end + end + local l_in = assert(self.input.rx, "No input link on rx.") + local l_out = assert(self.output.trunk, "No output link on trunk.") + for i = 1, link.nreadable(l_in) do + local p = link.receive(l_in) + if p.length < MIN_SIZE + or (mac and C.memcmp(mac, p.data+SRC_OFFSET, ADDRESS_SIZE) ~= 0) + then + packet.free(p) + else + link.transmit(l_out, p) + end + end +end diff --git a/src/apps/io/io.lua b/src/apps/io/io.lua new file mode 100644 index 0000000000..41d62b49b0 --- /dev/null +++ b/src/apps/io/io.lua @@ -0,0 +1,164 @@ +-- Use of this source code is governed by the Apache 2.0 license; see COPYING. + +-- ABSTRACT +-- +-- The IOControl and IO macro apps implement the following interface: +-- +-- config.app(c, "ctrl", IOControl, +-- {pciaddr="01:00.0", +-- queues = {{id="a", macaddr="10:10:10:10:10:10", vlan=42, buckets=2}, +-- {id="b", macaddr="20:20:20:20:20:20", vlan=43}}}) +-- +-- config.app(c, "io", IO, {pciaddr="01:00.0", queue = "a", bucket = 1}) +-- +-- All keys except `queues', `id', `buckets', `queue', and `bucket' are driver +-- dependent. +-- +-- To add support for a PCI driver module it must be registered in +-- lib.hardware.pci. To add support for a virtual driver module it must be +-- registered in the `virtual_module' table below. +-- +-- The driver module must expose a `driver' variable that contains the app that +-- implements the queue driver. If a control app is required for queue setup, +-- the driver must expose a `control' variable that contains the respective +-- app, which must accept the configuration argument passed to IOControl. +-- +-- Note that the `buckets' and `bucket' properties must default to 1. +-- +-- Finally, a configuration `formula' for the driver must be selected. See how +-- the `formula' table is populated below. +-- +-- FURTHER USAGE EXAMPLES +-- +-- config.app(c, "ctrl", IOControl, +-- {virtual="tap", +-- queues = {{id="a", ifname="foo"}}}) +-- +-- config.app(c, "tap_a", IO, {virtual="tap", queue="a"}) +-- +-- config.app(c, "ctrl", IOControl, +-- {virtual="emu", +-- queues = {{id="a", macaddr="10:10:10:10:10:10", buckets=2}}}) +-- +-- config.app(c, "emu_a", IO, {virtual="emu", queue="a", bucket=1}) + +module(..., package.seeall) +local lib = require("core.lib") +local pci = require("lib.hardware.pci") + +virtual_module = {} +formula = {} + +IOControl = { + config = { + pciaddr = {}, virtual = {}, + queues = {required=true} + } +} + +local queues = {}; setmetatable(queues, {__mode="k"}) -- Weak keys + +function IOControl:configure (c, name, conf) + local module + if conf.pciaddr then + module = require(pci.device_info(conf.pciaddr).driver) + elseif conf.virtual then + module = require(virtual_module[conf.virtual]) + else + error("Must supply one of: pciaddr, virtual") + end + if module.control then + config.app(c, name, module.control, conf) + end + queues[c] = queues[c] or {} + queues[c][conf.pciaddr or conf.virtual] = conf.queues +end + + +IO = { + config = { + pciaddr = {}, virtual = {}, + queue = {required=true}, + bucket = {} + } +} + +local function make_queueconf (c, conf) + local hub = conf.pciaddr or conf.virtual or fallback + local queueconf + for _, queuespec in ipairs(queues[c][hub]) do + if queuespec.id == conf.queue then + queueconf = lib.deepcopy(queuespec) + break + end + end + -- Delete IOControl specific keys, set pciaddr if applicable. + queueconf.id = nil + local buckets = queueconf.buckets + queueconf.buckets = nil + queueconf.pciaddr = conf.pciaddr + return queueconf, buckets +end + +function IO:configure (c, name, conf) + assert(conf.queue, "IO: conf needs `queue'") + local modulepath + if conf.pciaddr then + modulepath = pci.device_info(conf.pciaddr).driver + elseif conf.virtual then + modulepath = virtual_module[conf.virtual] + else + error("Must supply one of: pciaddr, virtual") + end + formula[modulepath](c, name, require(modulepath).driver, + conf.queue, conf.bucket, make_queueconf(c, conf)) +end + + +local function app_using_conf + (c, name, driver, queue, bucket, conf) + config.app(c, name, driver, conf) +end + +local function app_using_ifname + (c, name, driver, queue, bucket, conf) + config.app(c, name, driver, conf.ifname) +end + +local function app_using_everything + (c, name, driver, queue, bucket, conf, buckets) + config.app(c, name, driver, {pciaddr=conf.pciaddr, + queue=queue, + bucket=bucket, + buckets=buckets, + queueconf=conf}) +end + + +formula['apps.intel.intel_app'] = app_using_conf + +formula['apps.solarflare.solarflare'] = app_using_conf + +virtual_module.vhost = 'apps.vhost.vhost_user' +formula['apps.vhost.vhost_user'] = app_using_conf + +virtual_module.tap = 'apps.tap.tap' +formula['apps.tap.tap'] = app_using_ifname + +virtual_module.raw = 'apps.socket.raw' +formula['apps.socket.raw'] = app_using_ifname + +virtual_module.emu = 'apps.io.emu' +formula['apps.io.emu'] = app_using_everything + + +function selftest () + local c = config.new() + config.app(c, "IOControl", IOControl, + {queues = {{id="a", macaddr="60:50:40:40:20:10", buckets=2}}}) + config.app(c, "a1", IO, {queue="a", bucket=1}) + config.app(c, "a2", IO, {queue="a", bucket=2}) + engine.configure(c) + engine.report_apps() + engine.report_links() +end \ No newline at end of file diff --git a/src/apps/socket/raw.lua b/src/apps/socket/raw.lua index d668248072..3d25c2a2eb 100644 --- a/src/apps/socket/raw.lua +++ b/src/apps/socket/raw.lua @@ -15,6 +15,7 @@ local C = ffi.C local c, t = S.c, S.types.t RawSocket = {} +driver = RawSocket function RawSocket:new (ifname) assert(ifname) diff --git a/src/apps/tap/tap.lua b/src/apps/tap/tap.lua index 2218897031..f815292165 100644 --- a/src/apps/tap/tap.lua +++ b/src/apps/tap/tap.lua @@ -15,6 +15,7 @@ local os = require("os") local t = S.types.t Tap = { } +driver = Tap function Tap:new (name) assert(name, "missing tap interface name") diff --git a/src/apps/vhost/vhost_user.lua b/src/apps/vhost/vhost_user.lua index 72df66cba2..8d1babee46 100644 --- a/src/apps/vhost/vhost_user.lua +++ b/src/apps/vhost/vhost_user.lua @@ -26,6 +26,7 @@ require("apps.vhost.vhost_user_h") assert(ffi.sizeof("struct vhost_user_msg") == 276, "ABI error") VhostUser = {} +driver = VhostUser function VhostUser:new (args) local o = { state = 'init', From 998bdd396916461e53fbd123d5a8eecd79e70fb4 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 10 Nov 2016 18:26:27 +0100 Subject: [PATCH 03/14] apps.io: fallback to `emu'. # Conflicts: # src/apps/io/io.lua --- src/apps/io/io.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/apps/io/io.lua b/src/apps/io/io.lua index 41d62b49b0..55d1a7dc7a 100644 --- a/src/apps/io/io.lua +++ b/src/apps/io/io.lua @@ -48,6 +48,7 @@ local pci = require("lib.hardware.pci") virtual_module = {} formula = {} +local fallback = 'emu' IOControl = { config = { @@ -65,13 +66,13 @@ function IOControl:configure (c, name, conf) elseif conf.virtual then module = require(virtual_module[conf.virtual]) else - error("Must supply one of: pciaddr, virtual") + module = require(virtual_module[fallback]) end if module.control then config.app(c, name, module.control, conf) end queues[c] = queues[c] or {} - queues[c][conf.pciaddr or conf.virtual] = conf.queues + queues[c][conf.pciaddr or conf.virtual or fallback] = conf.queues end @@ -108,7 +109,7 @@ function IO:configure (c, name, conf) elseif conf.virtual then modulepath = virtual_module[conf.virtual] else - error("Must supply one of: pciaddr, virtual") + modulepath = virtual_module[fallback] end formula[modulepath](c, name, require(modulepath).driver, conf.queue, conf.bucket, make_queueconf(c, conf)) From bdde05457e80365f20f2b620b4ff29d84cd0a2df Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 9 Nov 2016 17:19:55 +0100 Subject: [PATCH 04/14] intel_app: have IOControl figure out vmdq setting. --- src/apps/intel/intel_app.lua | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index afd76809e5..90e279b738 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -32,9 +32,18 @@ Intel82599.__index = Intel82599 local C = ffi.C --- The `driver' variable is used as a reference to the driver class in --- order to interchangably use NIC drivers. +-- The `driver' and `control' variables are used as a reference to the driver +-- classes in order to interchangably use NIC drivers. driver = Intel82599 +control = {} +function control:configure (c, name, conf) + -- Ensure correct `vmdq' settings in conf + if #conf.queues > 1 then + for _, queue in ipairs(conf.queues) do + queue.vmdq = true + end + end +end -- table pciaddr => {pf, vflist} local devices = {} From 26cb9a524a1d62c45a031740bf66355ec4e75255 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 9 Nov 2016 17:25:44 +0100 Subject: [PATCH 05/14] nfvconfig: use apps.io instead of virtual_ether_mux. --- src/lib/io/virtual_ether_mux.lua | 97 ------------------------------ src/program/snabbnfv/nfvconfig.lua | 42 ++++++++++--- 2 files changed, 33 insertions(+), 106 deletions(-) delete mode 100644 src/lib/io/virtual_ether_mux.lua diff --git a/src/lib/io/virtual_ether_mux.lua b/src/lib/io/virtual_ether_mux.lua deleted file mode 100644 index 5fe69438d5..0000000000 --- a/src/lib/io/virtual_ether_mux.lua +++ /dev/null @@ -1,97 +0,0 @@ --- Use of this source code is governed by the Apache 2.0 license; see COPYING. - -module(..., package.seeall) -local pci = require("lib.hardware.pci") -local RawSocket = require("apps.socket.raw").RawSocket -local LearningBridge = require("apps.bridge.learning").bridge -local FloodingBridge = require("apps.bridge.flooding").bridge -local vlan = require("apps.vlan.vlan") -local basic_apps = require("apps.basic.basic_apps") -local Synth = require("apps.test.synth").Synth - -function configure (c, ports, io) - local links - if io and io.pci then - local device = pci.device_info(io.pci) - if device and (device.driver == 'apps.intel.intel_app' - or device.driver == 'apps.solarflare.solarflare') then - links = configureVMDq(c, device, ports) - else - error("Unknown device: "..io.pci) - end - else - local Switch = "Switch" - local switch_ports = {} - for i, port in ipairs(ports) do - switch_ports[i] = port_name(port) - end - local Trunk - if io and io.iface then - config.app(c, "TrunkIface", RawSocket, io.iface) - Trunk = {port = "TrunkIface", - input = "TrunkIface.rx", - output = "TrunkIface.tx"} - end - if io and io.bench then - config.app(c, "BenchSource", Synth, io.bench) - config.app(c, "BenchSink", basic_apps.Sink) - Trunk = {port = "TrunkBench", - input = "BenchSink.rx", - output = "BenchSource.tx"} - end - if Trunk then switch_ports[#switch_ports+1] = Trunk.port end - if #ports <= 2 then - config.app(c, Switch, FloodingBridge, {ports = switch_ports}) - else - config.app(c, Switch, LearningBridge, {ports = switch_ports}) - end - if Trunk then - config.link(c, Trunk.output.." -> "..Switch.."."..Trunk.port) - config.link(c, Switch.."."..Trunk.port.." -> "..Trunk.input) - end - links = {} - for i, port in ipairs(ports) do - local name = port_name(port) - local Switch_link = Switch.."."..name - local Port_tx, Port_rx = Switch_link, Switch_link - if port.vlan then - local VlanTag, VlanUntag = name.."_VlanTag", name.."_VlanUntag" - config.app(c, VlanTag, vlan.Tagger, {tag = port.vlan}) - config.link(c, VlanTag..".output -> "..Port_rx) - Port_rx = VlanTag..".input" - config.app(c, VlanUntag, vlan.Untagger, {tag = port.vlan}) - config.link(c, Port_tx.." -> "..VlanUntag..".input") - Port_tx = VlanUntag..".output" - end - links[i] = {input = Port_rx, output = Port_tx} - end - end - return links -end - --- Return name of port in . -function port_name (port_config) - return port_config.port_id:gsub("-", "_") -end - -function configureVMDq (c, device, ports) - local links = {} - for i, port in ipairs(ports) do - local name = port_name(port) - local NIC = name.."_NIC" - local vmdq = true - if not port.mac_address then - if #ports ~= 1 then - error("multiple ports defined but promiscuous mode requested for port: "..name) - end - vmdq = false - end - config.app(c, NIC, require(device.driver).driver, - {pciaddr = device.pciaddress, - vmdq = vmdq, - macaddr = port.mac_address, - vlan = port.vlan}) - links[i] = {input = NIC..".rx", output = NIC..".tx"} - end - return links -end diff --git a/src/program/snabbnfv/nfvconfig.lua b/src/program/snabbnfv/nfvconfig.lua index 884e6c9705..31f68042e3 100644 --- a/src/program/snabbnfv/nfvconfig.lua +++ b/src/program/snabbnfv/nfvconfig.lua @@ -2,13 +2,16 @@ module(...,package.seeall) +local IOControl = require("apps.io.io").IOControl +local IO = require("apps.io.io").IO local VhostUser = require("apps.vhost.vhost_user").VhostUser local PcapFilter = require("apps.packet_filter.pcap_filter").PcapFilter local RateLimiter = require("apps.rate_limiter.rate_limiter").RateLimiter local nd_light = require("apps.ipv6.nd_light").nd_light local L2TPv3 = require("apps.keyed_ipv6_tunnel.tunnel").SimpleKeyedTunnel local AES128gcm = require("apps.ipsec.esp").AES128gcm -local virtual_ether_mux = require("lib.io.virtual_ether_mux") +local Synth = require("apps.test.synth").Synth +local Sink = require("apps.basic.basic_apps").Sink local pci = require("lib.hardware.pci") local ffi = require("ffi") local C = ffi.C @@ -19,17 +22,26 @@ function port_name (port_config) return port_config.port_id:gsub("-", "_") end +function nic_queues (ports, soft_bench) + local queues = {} + for i, port in ipairs(ports) do + table.insert(queues, {id = port_name(port), + macaddr = port.mac_address, + vlan = port.vlan}) + end + if soft_bench then + table.insert(queues, {id = "__SoftBench", + macaddr = soft_bench.src}) + end + return queues +end + -- Compile app configuration from for and vhost_user . -- Optionally install source and sink. Returns configuration. function load (file, pciaddr, sockpath, soft_bench) local ports = lib.load_conf(file) local c = config.new() - local io_links - if pciaddr then - io_links = virtual_ether_mux.configure(c, ports, {pci = pciaddr}) - else - io_links = virtual_ether_mux.configure(c, ports, {bench = soft_bench}) - end + config.app(c, "NIC", IOControl, {pciaddr=pciaddr, queues=nic_queues(ports, soft_bench)}) for i,t in ipairs(ports) do -- Backwards compatibity / deprecated fields for deprecated, current in pairs({tx_police_gbps = "tx_police", @@ -114,8 +126,20 @@ function load (file, pciaddr, sockpath, soft_bench) config.link(c, RxLimit..".output -> "..VM_rx) VM_rx = RxLimit..".input" end - config.link(c, io_links[i].output.." -> "..VM_rx) - config.link(c, VM_tx.." -> "..io_links[i].input) + -- Finally, configure NIC queue and connect ends + local NIC = name.."_NIC" + config.app(c, NIC, IO, {pciaddr = pciaddr, queue=name}) + config.link(c, NIC..".tx".." -> "..VM_rx) + config.link(c, VM_tx.." -> "..NIC..".rx") + end + + -- Set up virtual packet generator if requested. + if soft_bench then + config.app(c, "BenchIO", IO, {pciaddr=pciaddr, queue="__SoftBench"}) + config.app(c, "BenchSource", Synth, io.bench) + config.app(c, "BenchSink", Sink) + config.link(c, "BenchSource.output -> BenchIO.rx") + config.link(c, "BenchIO.tx -> BenchSink.input") end -- Return configuration c. From b2e33120f7b890ff294d26f92ced6a47c41c6f5e Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 10 Nov 2016 18:19:47 +0100 Subject: [PATCH 06/14] apps.tap: support rx/rx ports --- src/apps/tap/tap.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apps/tap/tap.lua b/src/apps/tap/tap.lua index f815292165..0ddb3bbdde 100644 --- a/src/apps/tap/tap.lua +++ b/src/apps/tap/tap.lua @@ -45,7 +45,7 @@ function Tap:new (name) end function Tap:pull () - local l = self.output.output + local l = self.output.output or self.output.tx if l == nil then return end for i=1,engine.pull_npackets do local len, err = S.read(self.sock, self.pkt.data, C.PACKET_PAYLOAD_SIZE) @@ -72,7 +72,7 @@ function Tap:pull () end function Tap:push () - local l = self.input.input + local l = self.input.input or self.input.rx while not link.empty(l) do -- The socket write might of blocked so don't dequeue the packet from the link -- until the write has completed. From 3839a4bfb26482a39058b3a7cb5d3e1fe3b2b87b Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Fri, 11 Nov 2016 15:43:04 +0100 Subject: [PATCH 07/14] Revert "apps.tap: support rx/rx ports" This reverts commit b2e33120f7b890ff294d26f92ced6a47c41c6f5e. --- src/apps/tap/tap.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apps/tap/tap.lua b/src/apps/tap/tap.lua index 0ddb3bbdde..f815292165 100644 --- a/src/apps/tap/tap.lua +++ b/src/apps/tap/tap.lua @@ -45,7 +45,7 @@ function Tap:new (name) end function Tap:pull () - local l = self.output.output or self.output.tx + local l = self.output.output if l == nil then return end for i=1,engine.pull_npackets do local len, err = S.read(self.sock, self.pkt.data, C.PACKET_PAYLOAD_SIZE) @@ -72,7 +72,7 @@ function Tap:pull () end function Tap:push () - local l = self.input.input or self.input.rx + local l = self.input.input while not link.empty(l) do -- The socket write might of blocked so don't dequeue the packet from the link -- until the write has completed. From 2814b55cc88e35e3660df4916ee7654373417cbc Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Fri, 11 Nov 2016 15:43:22 +0100 Subject: [PATCH 08/14] Revert "intel_app: have IOControl figure out vmdq setting." This reverts commit bdde05457e80365f20f2b620b4ff29d84cd0a2df. --- src/apps/intel/intel_app.lua | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 90e279b738..afd76809e5 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -32,18 +32,9 @@ Intel82599.__index = Intel82599 local C = ffi.C --- The `driver' and `control' variables are used as a reference to the driver --- classes in order to interchangably use NIC drivers. +-- The `driver' variable is used as a reference to the driver class in +-- order to interchangably use NIC drivers. driver = Intel82599 -control = {} -function control:configure (c, name, conf) - -- Ensure correct `vmdq' settings in conf - if #conf.queues > 1 then - for _, queue in ipairs(conf.queues) do - queue.vmdq = true - end - end -end -- table pciaddr => {pf, vflist} local devices = {} From f22b5f839ff13a89a46f5040bc3d6c2853ea6dc6 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Fri, 11 Nov 2016 16:01:26 +0100 Subject: [PATCH 09/14] apps.io: deserved simplicity. --- src/apps/io/emu.lua | 68 +++++------- src/apps/io/io.lua | 163 ++++------------------------- src/program/snabbnfv/nfvconfig.lua | 46 ++++---- 3 files changed, 62 insertions(+), 215 deletions(-) diff --git a/src/apps/io/emu.lua b/src/apps/io/emu.lua index 24684f6a21..54452b8bd5 100644 --- a/src/apps/io/emu.lua +++ b/src/apps/io/emu.lua @@ -1,70 +1,54 @@ -- Use of this source code is governed by the Apache 2.0 license; see COPYING. module(..., package.seeall) +local io = require("apps.io.io") local FloodingBridge = require("apps.bridge.flooding").bridge local ethernet = require("lib.protocol.ethernet") local ipv6 = require("lib.protocol.ipv6") local murmur = require("lib.hash.murmur") local C = require("ffi").C - -control = { - config = { - virtual = {}, - queues = {required=true} - } +Emu = io.register('emu', {}) +Emu.config = { + device = {}, + queues = {required=true} } -local bridge = {}; setmetatable(bridge, {__mode="k"}) -- Weak keys - -function control:configure (c, name, conf) - bridge[c] = name - local ports = {} - for _, queuespec in ipairs(conf.queues) do - if not queuespec.buckets or queuespec.buckets == 1 then - table.insert(ports, queuespec.id) - else - for bucket = 1, queuespec.buckets do - table.insert(ports, queuespec.id.."_"..bucket) - end +function Emu:configure (c, name, conf) + local bridge = conf.device or name + local ports, mod = {}, 1 + for name, qconf in pairs(conf.queues) do + table.insert(ports, name) + if qconf.hash then + mod = math.max(qconf.hash, mod) end end config.app(c, name, FloodingBridge, {ports=ports}) + for name, qconf in pairs(conf.queues) do + config.app(c, name, Emu, qconf) + config.link(c, name..".trunk -> "..bridge.."."..name) + config.link(c, bridge.."."..name.." -> "..name..".trunk") + end end -driver = { +Emu = { config = { - queue = {required=true}, - bucket = {default=1}, - buckets = {default=1}, - queueconf = {required=true} + macaddr = {}, + hash = {default=1}, + mod = {default=1}, } } -Emu = {config = driver.config} - -function driver:configure (c, name, conf) - config.app(c, name, Emu, conf) - local port - if conf.buckets == 1 then - port = conf.queue - else - port = conf.queue.."_"..conf.bucket - end - config.link(c, name..".trunk -> "..bridge[c].."."..port) - config.link(c, bridge[c].."."..port.." -> "..name..".trunk") -end - function Emu:new (conf) local o = {} if conf.macaddr then o.mac = ethernet:pton(conf.macaddr) end - if conf.buckets > 1 then + if conf.mod > 1 then o.murmur = murmur.MurmurHash3_x86_32:new() - o.buckets = conf.buckets - o.bucket = conf.bucket + o.mod = conf.mod + o.hash = conf.hash end return setmetatable(o, {__index=Emu}) end @@ -80,14 +64,14 @@ function Emu:hash (p) end function Emu:push () - local mac, bucket, buckets = self.max, self.bucket, self.buckets + local mac, hash, mod = self.max, self.hash, self.mod local l_in = assert(self.input.trunk, "No input link on trunk.") local l_out = assert(self.output.tx, "No output link on tx.") for i = 1, link.nreadable(l_in) do local p = link.receive(l_in) if p.length < MIN_SIZE or (mac and C.memcmp(mac, p.data, ADDRESS_SIZE) ~= 0) - or (bucket and 1 + (self:hash(p) % buckets) ~= bucket) + or (hash and 1 + (self:hash(p) % mod) ~= hash) then packet.free(p) else diff --git a/src/apps/io/io.lua b/src/apps/io/io.lua index 55d1a7dc7a..01d0df070b 100644 --- a/src/apps/io/io.lua +++ b/src/apps/io/io.lua @@ -1,165 +1,38 @@ -- Use of this source code is governed by the Apache 2.0 license; see COPYING. --- ABSTRACT --- --- The IOControl and IO macro apps implement the following interface: --- --- config.app(c, "ctrl", IOControl, --- {pciaddr="01:00.0", --- queues = {{id="a", macaddr="10:10:10:10:10:10", vlan=42, buckets=2}, --- {id="b", macaddr="20:20:20:20:20:20", vlan=43}}}) --- --- config.app(c, "io", IO, {pciaddr="01:00.0", queue = "a", bucket = 1}) --- --- All keys except `queues', `id', `buckets', `queue', and `bucket' are driver --- dependent. --- --- To add support for a PCI driver module it must be registered in --- lib.hardware.pci. To add support for a virtual driver module it must be --- registered in the `virtual_module' table below. --- --- The driver module must expose a `driver' variable that contains the app that --- implements the queue driver. If a control app is required for queue setup, --- the driver must expose a `control' variable that contains the respective --- app, which must accept the configuration argument passed to IOControl. --- --- Note that the `buckets' and `bucket' properties must default to 1. --- --- Finally, a configuration `formula' for the driver must be selected. See how --- the `formula' table is populated below. --- --- FURTHER USAGE EXAMPLES --- --- config.app(c, "ctrl", IOControl, --- {virtual="tap", --- queues = {{id="a", ifname="foo"}}}) --- --- config.app(c, "tap_a", IO, {virtual="tap", queue="a"}) --- --- config.app(c, "ctrl", IOControl, --- {virtual="emu", --- queues = {{id="a", macaddr="10:10:10:10:10:10", buckets=2}}}) --- --- config.app(c, "emu_a", IO, {virtual="emu", queue="a", bucket=1}) +-- config.app(c, "IO", IO, {type="pci", device="01:00.0", +-- queues={a = {...}, ...}}) module(..., package.seeall) -local lib = require("core.lib") -local pci = require("lib.hardware.pci") -virtual_module = {} -formula = {} -local fallback = 'emu' - -IOControl = { - config = { - pciaddr = {}, virtual = {}, - queues = {required=true} - } -} - -local queues = {}; setmetatable(queues, {__mode="k"}) -- Weak keys - -function IOControl:configure (c, name, conf) - local module - if conf.pciaddr then - module = require(pci.device_info(conf.pciaddr).driver) - elseif conf.virtual then - module = require(virtual_module[conf.virtual]) - else - module = require(virtual_module[fallback]) - end - if module.control then - config.app(c, name, module.control, conf) - end - queues[c] = queues[c] or {} - queues[c][conf.pciaddr or conf.virtual or fallback] = conf.queues +local types = {} +function register (type, app) + assert(app, "Must supply app") + assert(not types[type], "Duplicate IO type: "..type) + types[type] = app + return app end - IO = { config = { - pciaddr = {}, virtual = {}, - queue = {required=true}, - bucket = {} + type = {default='emu'}, + device = {}, + queues = {required=true} } } -local function make_queueconf (c, conf) - local hub = conf.pciaddr or conf.virtual or fallback - local queueconf - for _, queuespec in ipairs(queues[c][hub]) do - if queuespec.id == conf.queue then - queueconf = lib.deepcopy(queuespec) - break - end - end - -- Delete IOControl specific keys, set pciaddr if applicable. - queueconf.id = nil - local buckets = queueconf.buckets - queueconf.buckets = nil - queueconf.pciaddr = conf.pciaddr - return queueconf, buckets -end - function IO:configure (c, name, conf) - assert(conf.queue, "IO: conf needs `queue'") - local modulepath - if conf.pciaddr then - modulepath = pci.device_info(conf.pciaddr).driver - elseif conf.virtual then - modulepath = virtual_module[conf.virtual] - else - modulepath = virtual_module[fallback] - end - formula[modulepath](c, name, require(modulepath).driver, - conf.queue, conf.bucket, make_queueconf(c, conf)) -end - - -local function app_using_conf - (c, name, driver, queue, bucket, conf) - config.app(c, name, driver, conf) + local app = assert(types[conf.type], "Unknown IO type: "..conf.type) + config.app(c, name, app, {device=conf.device, queues=conf.queues}) end -local function app_using_ifname - (c, name, driver, queue, bucket, conf) - config.app(c, name, driver, conf.ifname) -end - -local function app_using_everything - (c, name, driver, queue, bucket, conf, buckets) - config.app(c, name, driver, {pciaddr=conf.pciaddr, - queue=queue, - bucket=bucket, - buckets=buckets, - queueconf=conf}) -end - - -formula['apps.intel.intel_app'] = app_using_conf - -formula['apps.solarflare.solarflare'] = app_using_conf - -virtual_module.vhost = 'apps.vhost.vhost_user' -formula['apps.vhost.vhost_user'] = app_using_conf - -virtual_module.tap = 'apps.tap.tap' -formula['apps.tap.tap'] = app_using_ifname - -virtual_module.raw = 'apps.socket.raw' -formula['apps.socket.raw'] = app_using_ifname - -virtual_module.emu = 'apps.io.emu' -formula['apps.io.emu'] = app_using_everything - - function selftest () + require("apps.io.emu") local c = config.new() - config.app(c, "IOControl", IOControl, - {queues = {{id="a", macaddr="60:50:40:40:20:10", buckets=2}}}) - config.app(c, "a1", IO, {queue="a", bucket=1}) - config.app(c, "a2", IO, {queue="a", bucket=2}) + config.app(c, "IO", IO, + {queues = {a = {macaddr="60:50:40:40:20:10", hash=1}, + b = {macaddr="60:50:40:40:20:10", hash=2}}}) engine.configure(c) engine.report_apps() engine.report_links() -end \ No newline at end of file +end diff --git a/src/program/snabbnfv/nfvconfig.lua b/src/program/snabbnfv/nfvconfig.lua index 31f68042e3..54139a6bdc 100644 --- a/src/program/snabbnfv/nfvconfig.lua +++ b/src/program/snabbnfv/nfvconfig.lua @@ -2,7 +2,6 @@ module(...,package.seeall) -local IOControl = require("apps.io.io").IOControl local IO = require("apps.io.io").IO local VhostUser = require("apps.vhost.vhost_user").VhostUser local PcapFilter = require("apps.packet_filter.pcap_filter").PcapFilter @@ -22,26 +21,27 @@ function port_name (port_config) return port_config.port_id:gsub("-", "_") end -function nic_queues (ports, soft_bench) - local queues = {} - for i, port in ipairs(ports) do - table.insert(queues, {id = port_name(port), - macaddr = port.mac_address, - vlan = port.vlan}) - end - if soft_bench then - table.insert(queues, {id = "__SoftBench", - macaddr = soft_bench.src}) - end - return queues -end - -- Compile app configuration from for and vhost_user . -- Optionally install source and sink. Returns configuration. function load (file, pciaddr, sockpath, soft_bench) local ports = lib.load_conf(file) local c = config.new() - config.app(c, "NIC", IOControl, {pciaddr=pciaddr, queues=nic_queues(ports, soft_bench)}) + local NIC_suffix = "_NIC" + local queues = {} + for _, port in ipairs(ports) do + local NIC = port_name(port)..NIC_suffix + queues[NIC] = {macaddr = port.mac_address, vlan = port.vlan} + end + -- Set up virtual packet generator if requested. + if soft_bench then + assert(not queues["__SoftBench"]) + queues["__SoftBench"] {macaddr = soft_bench.src} + config.app(c, "BenchSource", Synth, io.bench) + config.app(c, "BenchSink", Sink) + config.link(c, "BenchSource.output -> __SoftBench.rx") + config.link(c, "__SoftBench.tx -> BenchSink.input") + end + config.app(c, "IO", IO, {type=(pciaddr and 'pci'), device=pciaddr, queues=queues}) for i,t in ipairs(ports) do -- Backwards compatibity / deprecated fields for deprecated, current in pairs({tx_police_gbps = "tx_police", @@ -126,22 +126,12 @@ function load (file, pciaddr, sockpath, soft_bench) config.link(c, RxLimit..".output -> "..VM_rx) VM_rx = RxLimit..".input" end - -- Finally, configure NIC queue and connect ends - local NIC = name.."_NIC" - config.app(c, NIC, IO, {pciaddr = pciaddr, queue=name}) + -- Finally, connect ends to I/O port + local NIC = name..NIC_suffix config.link(c, NIC..".tx".." -> "..VM_rx) config.link(c, VM_tx.." -> "..NIC..".rx") end - -- Set up virtual packet generator if requested. - if soft_bench then - config.app(c, "BenchIO", IO, {pciaddr=pciaddr, queue="__SoftBench"}) - config.app(c, "BenchSource", Synth, io.bench) - config.app(c, "BenchSink", Sink) - config.link(c, "BenchSource.output -> BenchIO.rx") - config.link(c, "BenchIO.tx -> BenchSink.input") - end - -- Return configuration c. return c end From 10cdbebf74943ca498a6d137ac1154b842ef1921 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Fri, 11 Nov 2016 16:02:06 +0100 Subject: [PATCH 10/14] apps.io: add support for pci type and intel_app. --- src/apps/intel/intel_app.lua | 15 +++++++++++++++ src/lib/hardware/pci.lua | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index afd76809e5..91bd4c2b03 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -35,6 +35,21 @@ local C = ffi.C -- The `driver' variable is used as a reference to the driver class in -- order to interchangably use NIC drivers. driver = Intel82599 +-- The `io' variable is used by lib.hardware.pci to forward queue configuration +-- to NIC drivers. +io = {} +function io:configure (c, _, conf) + local nqueues, vmdq = 0, false + for _ in pairs(conf.queues) do + nqueues = nqueues + 1 + if nqueues > 1 then vmdq = true; break end + end + for name, qconf in pairs(conf.queues) do + qconf.pciaddr = conf.device + qconf.vmdq = vmdq + config.app(c, name, Intel82599, qconf) + end +end -- table pciaddr => {pf, vflist} local devices = {} diff --git a/src/lib/hardware/pci.lua b/src/lib/hardware/pci.lua index 3c3fdf0c09..d0b5f5d2dd 100644 --- a/src/lib/hardware/pci.lua +++ b/src/lib/hardware/pci.lua @@ -2,6 +2,7 @@ module(...,package.seeall) +local io = require("apps.io.io") local ffi = require("ffi") local C = ffi.C local S = require("syscall") @@ -218,3 +219,9 @@ function print_device_summary () print(fmt:format(unpack(values))) end end + + +PCIDriver = io.register('pci', {}) +function PCIDriver:configure (c, name, conf) + require(device_info(conf.device).driver).io:configure(c, name, conf) +end From d75b4181670fae29626792a443384d4cb5543fe6 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Fri, 11 Nov 2016 16:26:42 +0100 Subject: [PATCH 11/14] Revert leftovers from 3f61110f106124dad086ef0cab2f2243fd930e54 --- src/apps/socket/raw.lua | 1 - src/apps/tap/tap.lua | 1 - src/apps/vhost/vhost_user.lua | 1 - 3 files changed, 3 deletions(-) diff --git a/src/apps/socket/raw.lua b/src/apps/socket/raw.lua index 3d25c2a2eb..d668248072 100644 --- a/src/apps/socket/raw.lua +++ b/src/apps/socket/raw.lua @@ -15,7 +15,6 @@ local C = ffi.C local c, t = S.c, S.types.t RawSocket = {} -driver = RawSocket function RawSocket:new (ifname) assert(ifname) diff --git a/src/apps/tap/tap.lua b/src/apps/tap/tap.lua index f815292165..2218897031 100644 --- a/src/apps/tap/tap.lua +++ b/src/apps/tap/tap.lua @@ -15,7 +15,6 @@ local os = require("os") local t = S.types.t Tap = { } -driver = Tap function Tap:new (name) assert(name, "missing tap interface name") diff --git a/src/apps/vhost/vhost_user.lua b/src/apps/vhost/vhost_user.lua index 8d1babee46..72df66cba2 100644 --- a/src/apps/vhost/vhost_user.lua +++ b/src/apps/vhost/vhost_user.lua @@ -26,7 +26,6 @@ require("apps.vhost.vhost_user_h") assert(ffi.sizeof("struct vhost_user_msg") == 276, "ABI error") VhostUser = {} -driver = VhostUser function VhostUser:new (args) local o = { state = 'init', From 4a47c6530e37d10457abe487ff8e5f219004fc6a Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Mon, 14 Nov 2016 14:31:11 +0100 Subject: [PATCH 12/14] intel_app: correctly set vmdq, and detect promiscuous conflicts in queues. --- src/apps/intel/intel_app.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 91bd4c2b03..8fefce6c31 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -38,15 +38,19 @@ driver = Intel82599 -- The `io' variable is used by lib.hardware.pci to forward queue configuration -- to NIC drivers. io = {} -function io:configure (c, _, conf) +function io:configure (c, io, conf) local nqueues, vmdq = 0, false for _ in pairs(conf.queues) do nqueues = nqueues + 1 if nqueues > 1 then vmdq = true; break end end for name, qconf in pairs(conf.queues) do + if not qconf.macaddr and vmdq then + error(io..": multiple ports defined, ".. + "but promiscuous mode requested for queue: "..name) + end qconf.pciaddr = conf.device - qconf.vmdq = vmdq + qconf.vmdq = vmdq or (not not qconf.macaddr) config.app(c, name, Intel82599, qconf) end end From 02d6c179c33b19a6ae49bda7ab42a014fc0a501f Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 16 Nov 2016 15:07:06 +0100 Subject: [PATCH 13/14] apps.io: resolve name conflict with standard Lua io module. --- src/apps/intel/intel_app.lua | 8 ++++---- src/apps/io/{io.lua => common.lua} | 0 src/apps/io/emu.lua | 4 ++-- src/lib/hardware/pci.lua | 6 +++--- src/program/snabbnfv/nfvconfig.lua | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) rename src/apps/io/{io.lua => common.lua} (100%) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 8fefce6c31..63677dbe4b 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -35,10 +35,10 @@ local C = ffi.C -- The `driver' variable is used as a reference to the driver class in -- order to interchangably use NIC drivers. driver = Intel82599 --- The `io' variable is used by lib.hardware.pci to forward queue configuration --- to NIC drivers. -io = {} -function io:configure (c, io, conf) +-- The `control' variable is used by lib.hardware.pci to forward queue +-- configuration to NIC drivers. +control = {} +function control:configure (c, io, conf) local nqueues, vmdq = 0, false for _ in pairs(conf.queues) do nqueues = nqueues + 1 diff --git a/src/apps/io/io.lua b/src/apps/io/common.lua similarity index 100% rename from src/apps/io/io.lua rename to src/apps/io/common.lua diff --git a/src/apps/io/emu.lua b/src/apps/io/emu.lua index 54452b8bd5..a81a8d22bf 100644 --- a/src/apps/io/emu.lua +++ b/src/apps/io/emu.lua @@ -1,14 +1,14 @@ -- Use of this source code is governed by the Apache 2.0 license; see COPYING. module(..., package.seeall) -local io = require("apps.io.io") +local common_io = require("apps.io.common") local FloodingBridge = require("apps.bridge.flooding").bridge local ethernet = require("lib.protocol.ethernet") local ipv6 = require("lib.protocol.ipv6") local murmur = require("lib.hash.murmur") local C = require("ffi").C -Emu = io.register('emu', {}) +Emu = common_io.register('emu', {}) Emu.config = { device = {}, queues = {required=true} diff --git a/src/lib/hardware/pci.lua b/src/lib/hardware/pci.lua index d0b5f5d2dd..31c2f468d0 100644 --- a/src/lib/hardware/pci.lua +++ b/src/lib/hardware/pci.lua @@ -2,7 +2,7 @@ module(...,package.seeall) -local io = require("apps.io.io") +local common_io = require("apps.io.common") local ffi = require("ffi") local C = ffi.C local S = require("syscall") @@ -221,7 +221,7 @@ function print_device_summary () end -PCIDriver = io.register('pci', {}) +PCIDriver = common_io.register('pci', {}) function PCIDriver:configure (c, name, conf) - require(device_info(conf.device).driver).io:configure(c, name, conf) + require(device_info(conf.device).driver).control:configure(c, name, conf) end diff --git a/src/program/snabbnfv/nfvconfig.lua b/src/program/snabbnfv/nfvconfig.lua index 54139a6bdc..37b8d6df51 100644 --- a/src/program/snabbnfv/nfvconfig.lua +++ b/src/program/snabbnfv/nfvconfig.lua @@ -2,7 +2,7 @@ module(...,package.seeall) -local IO = require("apps.io.io").IO +local IO = require("apps.io.common").IO local VhostUser = require("apps.vhost.vhost_user").VhostUser local PcapFilter = require("apps.packet_filter.pcap_filter").PcapFilter local RateLimiter = require("apps.rate_limiter.rate_limiter").RateLimiter From 16858aa6baf479410cfb1f3ba0033ee86312433c Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 17 Nov 2016 16:13:54 +0100 Subject: [PATCH 14/14] apps.io: centralize polyfill implementations in apps.io.common. --- src/apps/intel/intel_app.lua | 19 ----------- src/apps/io/common.lua | 64 +++++++++++++++++++++++++++++++----- src/apps/io/emu.lua | 26 --------------- src/lib/hardware/pci.lua | 7 ---- 4 files changed, 55 insertions(+), 61 deletions(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 63677dbe4b..afd76809e5 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -35,25 +35,6 @@ local C = ffi.C -- The `driver' variable is used as a reference to the driver class in -- order to interchangably use NIC drivers. driver = Intel82599 --- The `control' variable is used by lib.hardware.pci to forward queue --- configuration to NIC drivers. -control = {} -function control:configure (c, io, conf) - local nqueues, vmdq = 0, false - for _ in pairs(conf.queues) do - nqueues = nqueues + 1 - if nqueues > 1 then vmdq = true; break end - end - for name, qconf in pairs(conf.queues) do - if not qconf.macaddr and vmdq then - error(io..": multiple ports defined, ".. - "but promiscuous mode requested for queue: "..name) - end - qconf.pciaddr = conf.device - qconf.vmdq = vmdq or (not not qconf.macaddr) - config.app(c, name, Intel82599, qconf) - end -end -- table pciaddr => {pf, vflist} local devices = {} diff --git a/src/apps/io/common.lua b/src/apps/io/common.lua index 01d0df070b..f0680adedf 100644 --- a/src/apps/io/common.lua +++ b/src/apps/io/common.lua @@ -5,13 +5,9 @@ module(..., package.seeall) -local types = {} -function register (type, app) - assert(app, "Must supply app") - assert(not types[type], "Duplicate IO type: "..type) - types[type] = app - return app -end +-- Maps type names to implementations +local type = {} + IO = { config = { @@ -22,10 +18,60 @@ IO = { } function IO:configure (c, name, conf) - local app = assert(types[conf.type], "Unknown IO type: "..conf.type) - config.app(c, name, app, {device=conf.device, queues=conf.queues}) + local impl = assert(type[conf.type], "Unknown IO type: "..conf.type) + impl(c, name, conf.device, conf.queues) +end + + +function type.emu (c, name, device, queues) + local FloodingBridge = require("apps.bridge.flooding").bridge + local Emu = require("apps.io.emu").Emu + local bridge = device or name + local ports, mod = {}, 1 + for name, queue in pairs(queues) do + table.insert(ports, name) + if queue.hash then + mod = math.max(queue.hash, mod) + end + end + config.app(c, name, FloodingBridge, {ports=ports}) + for name, queue in pairs(queues) do + config.app(c, name, Emu, queue) + config.link(c, name..".trunk -> "..bridge.."."..name) + config.link(c, bridge.."."..name.." -> "..name..".trunk") + end +end + + +-- Maps PCI driver to implementations +local driver = {} + +function type.pci (c, name, device, queues) + local pci = require("lib.hardware.pci") + local impl = assert(driver[pci.device_info(device).driver], + "Unsupported PCI device: "..device) + impl(c, name, device, queues) end +driver['apps.intel.intel_app'] = function (c, name, device, queues) + local Intel82599 = require("apps.intel.intel_app").Intel82599 + local nqueues, vmdq = 0, false + for _ in pairs(queues) do + nqueues = nqueues + 1 + if nqueues > 1 then vmdq = true; break end + end + for name, queue in pairs(queues) do + if not queue.macaddr and vmdq then + error(io..": multiple ports defined, ".. + "but promiscuous mode requested for queue: "..name) + end + queue.pciaddr = device + queue.vmdq = vmdq or (not not queue.macaddr) + config.app(c, name, Intel82599, queue) + end +end + + function selftest () require("apps.io.emu") local c = config.new() diff --git a/src/apps/io/emu.lua b/src/apps/io/emu.lua index a81a8d22bf..8ff2fb2b7a 100644 --- a/src/apps/io/emu.lua +++ b/src/apps/io/emu.lua @@ -1,37 +1,11 @@ -- Use of this source code is governed by the Apache 2.0 license; see COPYING. module(..., package.seeall) -local common_io = require("apps.io.common") -local FloodingBridge = require("apps.bridge.flooding").bridge local ethernet = require("lib.protocol.ethernet") local ipv6 = require("lib.protocol.ipv6") local murmur = require("lib.hash.murmur") local C = require("ffi").C -Emu = common_io.register('emu', {}) -Emu.config = { - device = {}, - queues = {required=true} -} - -function Emu:configure (c, name, conf) - local bridge = conf.device or name - local ports, mod = {}, 1 - for name, qconf in pairs(conf.queues) do - table.insert(ports, name) - if qconf.hash then - mod = math.max(qconf.hash, mod) - end - end - config.app(c, name, FloodingBridge, {ports=ports}) - for name, qconf in pairs(conf.queues) do - config.app(c, name, Emu, qconf) - config.link(c, name..".trunk -> "..bridge.."."..name) - config.link(c, bridge.."."..name.." -> "..name..".trunk") - end -end - - Emu = { config = { macaddr = {}, diff --git a/src/lib/hardware/pci.lua b/src/lib/hardware/pci.lua index 31c2f468d0..3c3fdf0c09 100644 --- a/src/lib/hardware/pci.lua +++ b/src/lib/hardware/pci.lua @@ -2,7 +2,6 @@ module(...,package.seeall) -local common_io = require("apps.io.common") local ffi = require("ffi") local C = ffi.C local S = require("syscall") @@ -219,9 +218,3 @@ function print_device_summary () print(fmt:format(unpack(values))) end end - - -PCIDriver = common_io.register('pci', {}) -function PCIDriver:configure (c, name, conf) - require(device_info(conf.device).driver).control:configure(c, name, conf) -end