From b34c3eee2bf5cb05a6f47c0ed9e318632c8dc534 Mon Sep 17 00:00:00 2001 From: Luke Gorrie Date: Mon, 22 Feb 2016 13:06:18 +0000 Subject: [PATCH 01/39] engine: Set shm path to "app/$name" Set the shared memory path (shm.path) to a private namespace for each app with prefix "app/$name". This means that apps can create shm objects such as counters and by default these will appear in a local namespace for that app. --- src/core/app.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/app.lua b/src/core/app.lua index 35a5305631..099fc3125a 100644 --- a/src/core/app.lua +++ b/src/core/app.lua @@ -5,6 +5,7 @@ local lib = require("core.lib") local link = require("core.link") local config = require("core.config") local timer = require("core.timer") +local shm = require("core.shm") local counter = require("core.counter") local zone = require("jit.zone") local ffi = require("ffi") @@ -65,6 +66,8 @@ end -- Run app:methodname() in protected mode (pcall). If it throws an -- error app will be marked as dead and restarted eventually. local function with_restart (app, method) + local oldshm = shm.path + shm.path = app.shmpath if use_restart then -- Run fn in protected mode using pcall. local status, err = pcall(method, app) @@ -75,6 +78,7 @@ local function with_restart (app, method) else method(app) end + shm.path = oldshm end -- Restart dead apps. @@ -162,7 +166,11 @@ function apply_config_actions (actions, conf) function ops.start (name) local class = conf.apps[name].class local arg = conf.apps[name].arg + local shmpath, shmorig = "app/"..name, shm.path + shm.path = shmpath local app = class:new(arg) + shm.path = shmorig + local shmpath = "app/"..name if type(app) ~= 'table' then error(("bad return value from app '%s' start() method: %s"):format( name, tostring(app))) @@ -171,6 +179,7 @@ function apply_config_actions (actions, conf) app.appname = name app.output = {} app.input = {} + app.shmpath = shmpath new_app_table[name] = app table.insert(new_app_array, app) app_name_to_index[name] = #new_app_array From 94ff2347175cc2e56153f0af3d16f9af86c2a367 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Tue, 12 Apr 2016 19:41:12 +0200 Subject: [PATCH 02/39] Amendments to #766: - Use "apps/" instead of "app/" for uniformity - Set shm path to "apps/$name" when calling `app:stop' too - Unlink "apps/$name" after `app:stop' using `shm.unlink' - Add a test case to core.app selftest --- src/core/app.lua | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/core/app.lua b/src/core/app.lua index 099fc3125a..5ff7d476eb 100644 --- a/src/core/app.lua +++ b/src/core/app.lua @@ -156,7 +156,13 @@ function apply_config_actions (actions, conf) -- Table of functions that execute config actions local ops = {} function ops.stop (name) - if app_table[name].stop then app_table[name]:stop() end + if app_table[name].stop then + local shmorig = shm.path + shm.path = app_table[name].shmpath + app_table[name]:stop() + shm.path = shmorig + shm.unlink(app_table[name].shmpath) + end end function ops.keep (name) new_app_table[name] = app_table[name] @@ -166,11 +172,10 @@ function apply_config_actions (actions, conf) function ops.start (name) local class = conf.apps[name].class local arg = conf.apps[name].arg - local shmpath, shmorig = "app/"..name, shm.path + local shmpath, shmorig = "apps/"..name, shm.path shm.path = shmpath local app = class:new(arg) shm.path = shmorig - local shmpath = "app/"..name if type(app) ~= 'table' then error(("bad return value from app '%s' start() method: %s"):format( name, tostring(app))) @@ -492,6 +497,34 @@ function selftest () assert(app_table.app3 == orig_app3) -- should be the same main({duration = 4, report = {showapps = true}}) assert(app_table.app3 ~= orig_app3) -- should be restarted + -- Test shm.path management + print("shm.path management") + local S = require("syscall") + local App4 = {zone="test"} + function App4:new () + local c = counter.open('test') + counter.set(c, 42) + counter.commit() + return setmetatable({test_counter = c}, + {__index = App4}) + end + function App4:pull () + assert(counter.read(self.test_counter) == 42, "Invalid counter value") + counter.add(self.test_counter) + end + function App4:stop () + assert(counter.read(self.test_counter) == 43, "Invalid counter value") + counter.delete('test') + end + local c_counter = config.new() + config.app(c_counter, "App4", App4) + configure(c_counter) + main({done = function () return app_table.App4.test_counter end}) + assert(S.stat(shm.root.."/"..shm.resolve("apps/App4/test")), + "Missing : apps/App4/test") + configure(config.new()) + assert(not S.stat(shm.root.."/"..shm.resolve("apps/App4")), + "Failed to unlink apps/App4") print("OK") end From fad0f43921d8f226f199f1cbaed02e5e6efc6d30 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 13 Apr 2016 16:44:14 +0200 Subject: [PATCH 03/39] core.counter: Qualify counter names using `shm.resolve'. --- src/core/counter.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/core/counter.lua b/src/core/counter.lua index a479df4040..881cea5717 100644 --- a/src/core/counter.lua +++ b/src/core/counter.lua @@ -44,7 +44,8 @@ local private = {} local numbers = {} -- name -> number function open (name, readonly) - if numbers[name] then error("counter already opened: " .. name) end + local qname = shm.resolve(name) + if numbers[qname] then error("counter already opened: " .. qname) end local n = #public+1 if readonly then public[n] = shm.open(name, counter_t, readonly) @@ -53,13 +54,14 @@ function open (name, readonly) public[n] = shm.create(name, counter_t) private[n] = ffi.new(counter_t) end - numbers[name] = n + numbers[qname] = n return private[n] end function delete (name) - local number = numbers[name] - if not number then error("counter not found for deletion: " .. name) end + local qname = shm.resolve(name) + local number = numbers[qname] + if not number then error("counter not found for deletion: " .. qname) end -- Free shm object shm.unmap(public[number]) -- If we "own" the counter for writing then we unlink it too. @@ -67,7 +69,7 @@ function delete (name) shm.unlink(name) end -- Free local state - numbers[name] = false + numbers[qname] = false public[number] = false private[number] = false end From 7ed4ed0383bb24fd780fb6ecda3197f5c1106165 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Tue, 12 Apr 2016 20:21:33 +0200 Subject: [PATCH 04/39] snabb top: add `--app' option to print app counters. --- src/program/top/README | 8 ++++-- src/program/top/top.lua | 61 +++++++++++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/program/top/README b/src/program/top/README index 365c357326..66471f390c 100644 --- a/src/program/top/README +++ b/src/program/top/README @@ -3,10 +3,12 @@ Usage: -h, --help Print usage information. + -a, --app + Print counters of app by and exit. -Display realtime performance statistics for a running Snabb -instance with . If is not supplied and there is only one Snabb -Switch instance, top will connect to that instance. +Display realtime performance statistics for a running Snabb instance with +. If is not supplied and there is only one Snabb instance, top will +connect to that instance. The following global metrics will be displayed: diff --git a/src/program/top/top.lua b/src/program/top/top.lua index 6617027b1b..f66af6df0d 100644 --- a/src/program/top/top.lua +++ b/src/program/top/top.lua @@ -12,20 +12,57 @@ local histogram = require("core.histogram") local usage = require("program.top.README_inc") local long_opts = { - help = "h" + help = "h", app = "a" } function clearterm () io.write('\027[2J') end function run (args) local opt = {} + local app_name = nil function opt.h (arg) print(usage) main.exit(1) end - args = lib.dogetopt(args, opt, "h", long_opts) + function opt.a (arg) app_name = arg end + args = lib.dogetopt(args, opt, "ha:", long_opts) if #args > 1 then print(usage) main.exit(1) end local target_pid = args[1] - local instance_tree = "//"..(select_snabb_instance(target_pid)) + if app_name then app(select_snabb_instance(target_pid), app_name) + else top(select_snabb_instance(target_pid)) end +end + +function select_snabb_instance (pid) + local instances = shm.children("//") + if pid then + -- Try to use given pid + for _, instance in ipairs(instances) do + if instance == pid then return pid end + end + print("No such Snabb instance: "..pid) + elseif #instances == 2 then + -- Two means one is us, so we pick the other. + local own_pid = tostring(S.getpid()) + if instances[1] == own_pid then return instances[2] + else return instances[1] end + elseif #instances == 1 then print("No Snabb instance found.") + else print("Multple Snabb instances found. Select one.") end + os.exit(1) +end + +function app (instance_pid, app_name) + local app_tree = "//"..instance_pid.."/apps/"..app_name + local cnames = shm.children(app_tree) + table.sort(cnames, function (a, b) return a < b end) + for _, cname in ipairs(cnames) do + local cpath = app_tree.."/"..cname + local value = counter.read(counter.open(cpath, 'readonly')) + print_row({30, 30}, {cname, lib.comma_value(value)}) + counter.delete(cpath) + end +end + +function top (instance_pid) + local instance_tree = "//"..instance_pid local counters = open_counters(instance_tree) local configs = 0 local last_stats = nil @@ -48,24 +85,6 @@ function run (args) end end -function select_snabb_instance (pid) - local instances = shm.children("//") - if pid then - -- Try to use given pid - for _, instance in ipairs(instances) do - if instance == pid then return pid end - end - print("No such Snabb instance: "..pid) - elseif #instances == 2 then - -- Two means one is us, so we pick the other. - local own_pid = tostring(S.getpid()) - if instances[1] == own_pid then return instances[2] - else return instances[1] end - elseif #instances == 1 then print("No Snabb instance found.") - else print("Multple Snabb instances found. Select one.") end - os.exit(1) -end - function open_counters (tree) local counters = {} for _, name in ipairs({"configs", "breaths", "frees", "freebytes"}) do From eb9005b5c5ec4aa637b8a327f556029f8ef4cf29 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 13 Apr 2016 17:24:44 +0200 Subject: [PATCH 05/39] snabb top: unlink own shm tree to avoid clutter. --- src/program/top/top.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/program/top/top.lua b/src/program/top/top.lua index f66af6df0d..54308c63f3 100644 --- a/src/program/top/top.lua +++ b/src/program/top/top.lua @@ -29,6 +29,7 @@ function run (args) if app_name then app(select_snabb_instance(target_pid), app_name) else top(select_snabb_instance(target_pid)) end + ordered_exit(0) end function select_snabb_instance (pid) @@ -46,7 +47,12 @@ function select_snabb_instance (pid) else return instances[1] end elseif #instances == 1 then print("No Snabb instance found.") else print("Multple Snabb instances found. Select one.") end - os.exit(1) + ordered_exit(1) +end + +function ordered_exit (value) + shm.unlink("//"..S.getpid()) -- Unlink own shm tree to avoid clutter + os.exit(value) end function app (instance_pid, app_name) From 5fbe0d6fba0a1c0fd785102bb6da0a184f95dfd5 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 14 Apr 2016 16:39:55 +0200 Subject: [PATCH 06/39] vhost_user: Add RFC 7223 app counters. --- src/apps/vhost/vhost_user.lua | 36 +++++++++++++++++++++++++++++++++++ src/lib/virtio/net_device.lua | 4 ++++ 2 files changed, 40 insertions(+) diff --git a/src/apps/vhost/vhost_user.lua b/src/apps/vhost/vhost_user.lua index 7890b495f5..96e82780eb 100644 --- a/src/apps/vhost/vhost_user.lua +++ b/src/apps/vhost/vhost_user.lua @@ -13,7 +13,9 @@ local lib = require("core.lib") local link = require("core.link") local main = require("core.main") local memory = require("core.memory") +local counter = require("core.counter") local pci = require("lib.hardware.pci") +local ethernet = require("lib.protocol.ethernet") local net_device= require("lib.virtio.net_device") local timer = require("core.timer") local ffi = require("ffi") @@ -52,6 +54,15 @@ function VhostUser:new (args) else self.qemu_connect = self.client_connect end + -- initialize counters + self.counters = {} + for _, name in ipairs({'type', 'discontinuity-time', + 'in-octets', 'in-unicast', 'in-multicast', 'in-discards', + 'out-octets', 'out-unicast', 'out-multicast'}) do + self.counters[name] = counter.open(name) + end + counter.set(self.counters['type'], 0x1001) -- Virtual interface + counter.set(self.counters['discontinuity-time'], C.get_unix_time()) return self end @@ -68,6 +79,9 @@ function VhostUser:stop() self:free_mem_table() if self.link_down_proc then self.link_down_proc() end + + -- delete counters + for name, _ in pairs(self.counters) do counter.delete(name) end end function VhostUser:pull () @@ -86,6 +100,28 @@ function VhostUser:push () end end +function VhostUser:tx_callback (p) + counter.add(self.counters['out-octets'], packet.length(p)) + if ethernet:is_mcast(packet.data(p)) then + counter.add(self.counters['out-multicast']) + else + counter.add(self.counters['out-unicast']) + end +end + +function VhostUser:rx_callback (p) + counter.add(self.counters['in-octets'], packet.length(p)) + if ethernet:is_mcast(packet.data(p)) then + counter.add(self.counters['in-multicast']) + else + counter.add(self.counters['in-unicast']) + end +end + +function VhostUser:rxdrop_callback (p) + counter.add(self.counters['in-discards']) +end + -- Try to connect to QEMU. function VhostUser:client_connect () return C.vhost_user_connect(self.socket_path) diff --git a/src/lib/virtio/net_device.lua b/src/lib/virtio/net_device.lua index 1f9f321191..0cbdcd13c3 100644 --- a/src/lib/virtio/net_device.lua +++ b/src/lib/virtio/net_device.lua @@ -163,9 +163,11 @@ function VirtioNetDevice:rx_packet_end(header_id, total_size, rx_p) rx_p.length - self.rx_hdr_csum_start, self.rx_hdr_csum_offset) end + self.owner:rx_callback(rx_p) link.transmit(l, rx_p) else debug("droprx", "len", rx_p.length) + self.owner:rxdrop_callback(rx_p) packet.free(rx_p) end self.virtq[self.ring_id]:put_buffer(header_id, total_size) @@ -253,6 +255,7 @@ function VirtioNetDevice:tx_buffer_add(tx_p, addr, len) end function VirtioNetDevice:tx_packet_end(header_id, total_size, tx_p) + self.owner:tx_callback(tx_p) packet.free(tx_p) self.virtq[self.ring_id]:put_buffer(header_id, total_size) end @@ -313,6 +316,7 @@ end function VirtioNetDevice:tx_packet_end_mrg_rxbuf(header_id, total_size, tx_p) -- free the packet only when all its data is processed if self.tx.finished then + self.owner:tx_callback(tx_p) packet.free(tx_p) self.tx.p = nil self.tx.data_sent = nil From 8bb3215bf759f6c53af07c5c13afd2ea60003334 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 14 Apr 2016 17:57:09 +0200 Subject: [PATCH 07/39] Intel_app: Add RFC 7223 app counters. --- src/apps/intel/intel_app.lua | 45 ++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 80cace3d9f..f104fad553 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -6,8 +6,10 @@ local zone = require("jit.zone") local basic_apps = require("apps.basic.basic_apps") local ffi = require("ffi") local lib = require("core.lib") +local counter = require("core.counter") local pci = require("lib.hardware.pci") local register = require("lib.hardware.register") +local macaddress = require("lib.macaddress") local intel10g = require("apps.intel.intel10g") local freelist = require("core.freelist") local receive, transmit, full, empty = link.receive, link.transmit, link.full, link.empty @@ -36,6 +38,7 @@ end -- Create an Intel82599 App for the device with 'pciaddress'. function Intel82599:new (arg) local conf = config.parse_app_arg(arg) + local self = setmetatable({counters = {}}, Intel82599) if conf.vmdq then if devices[conf.pciaddr] == nil then @@ -45,12 +48,26 @@ function Intel82599:new (arg) local poolnum = firsthole(dev.vflist)-1 local vf = dev.pf:new_vf(poolnum) dev.vflist[poolnum+1] = vf - return setmetatable({dev=vf:open(conf)}, Intel82599) + self.dev = vf:open(conf) else - local dev = intel10g.new_sf(conf):open() - if not dev then return null end - return setmetatable({dev=dev, zone="intel"}, Intel82599) + self.dev = assert(intel10g.new_sf(conf):open(), "Can not open device.") + self.zone = "intel" end + + self.counters['type'] = counter.open('type') + self.counters['discontinuity-time'] = counter.open('discontinuity-time') + self.counters['in-octets'] = counter.open('in-octets') + self.counters['in-discards'] = counter.open('in-discards') + self.counters['out-octets'] = counter.open('out-octets') + counter.set(self.counters['type'], 0x1000) -- Hardware interface + counter.set(self.counters['discontinuity-time'], C.get_unix_time()) + if conf.vmdq then + self.counters['phys-address'] = counter.open('phys-address') + counter.set(self.counters['phys-address'], + macaddress:new(conf.macaddr).bits) + end + + return self end function Intel82599:stop() @@ -71,6 +88,9 @@ function Intel82599:stop() if close_pf then close_pf:close() end + + -- delete counters + for name, _ in pairs(self.counters) do counter.delete(name) end end @@ -79,6 +99,11 @@ function Intel82599:reconfig(arg) assert((not not self.dev.pf) == (not not conf.vmdq), "Can't reconfig from VMDQ to single-port or viceversa") self.dev:reconfig(conf) + + if conf.vmdq then + counter.set(self.counters['phys-address'], + macaddress:new(conf.macaddr).bits) + end end -- Allocate receive buffers from the given freelist. @@ -96,6 +121,13 @@ function Intel82599:pull () transmit(l, self.dev:receive()) end self:add_receive_buffers() + if self.dev.rxstats then + counter.set(self.counters['in-octets'], + bit.lshift(self.dev.pf.qs.QBRC_H[self.dev.rxstats]()+0LL, 32) + + self.dev.pf.qs.QBRC_L[self.dev.rxstats]()) + counter.set(self.counters['in-discards'], + self.dev.pf.qs.QPRDC[self.dev.rxstats]()) + end end function Intel82599:add_receive_buffers () @@ -116,6 +148,11 @@ function Intel82599:push () end end self.dev:sync_transmit() + if self.dev.txstats then + counter.set(self.counters['out-octets'], + bit.lshift(self.dev.pf.qs.QBTC_H[self.dev.txstats]()+0LL, 32) + + self.dev.pf.qs.QBTC_L[self.dev.txstats]()) + end end -- Report on relevant status and statistics. From 7a5547859a92ba935ed93ef1d144f1a9d8a1ab39 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 14 Apr 2016 18:13:51 +0200 Subject: [PATCH 08/39] snabb top: Add --link parameter to list link counters. --- src/program/top/README | 3 ++- src/program/top/top.lua | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/program/top/README b/src/program/top/README index 66471f390c..ae91f6e4c4 100644 --- a/src/program/top/README +++ b/src/program/top/README @@ -4,7 +4,8 @@ Usage: -h, --help Print usage information. -a, --app - Print counters of app by and exit. + -l, --link + Print counters of app or link by and exit. Display realtime performance statistics for a running Snabb instance with . If is not supplied and there is only one Snabb instance, top will diff --git a/src/program/top/top.lua b/src/program/top/top.lua index 54308c63f3..3b1f57a600 100644 --- a/src/program/top/top.lua +++ b/src/program/top/top.lua @@ -12,7 +12,7 @@ local histogram = require("core.histogram") local usage = require("program.top.README_inc") local long_opts = { - help = "h", app = "a" + help = "h", app = "a", link = "l" } function clearterm () io.write('\027[2J') end @@ -20,15 +20,18 @@ function clearterm () io.write('\027[2J') end function run (args) local opt = {} local app_name = nil + local link_name = nil function opt.h (arg) print(usage) main.exit(1) end function opt.a (arg) app_name = arg end - args = lib.dogetopt(args, opt, "ha:", long_opts) + function opt.l (arg) link_name = arg end + args = lib.dogetopt(args, opt, "ha:l:", long_opts) if #args > 1 then print(usage) main.exit(1) end - local target_pid = args[1] + local target_pid = select_snabb_instance(args[1]) - if app_name then app(select_snabb_instance(target_pid), app_name) - else top(select_snabb_instance(target_pid)) end + if app_name then list_counters("//"..target_pid.."/apps/"..app_name) + elseif link_name then list_counters("//"..target_pid.."/counters/"..link_name) + else top(target_pid) end ordered_exit(0) end @@ -55,12 +58,11 @@ function ordered_exit (value) os.exit(value) end -function app (instance_pid, app_name) - local app_tree = "//"..instance_pid.."/apps/"..app_name - local cnames = shm.children(app_tree) +function list_counters (path) + local cnames = shm.children(path) table.sort(cnames, function (a, b) return a < b end) for _, cname in ipairs(cnames) do - local cpath = app_tree.."/"..cname + local cpath = path.."/"..cname local value = counter.read(counter.open(cpath, 'readonly')) print_row({30, 30}, {cname, lib.comma_value(value)}) counter.delete(cpath) From dde5da2ac77cda91ed806b68399d7680c92004e3 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 14 Apr 2016 19:17:20 +0200 Subject: [PATCH 09/39] core.app: Put app counters under "counters/", update snabb top. --- src/core/app.lua | 10 +++++----- src/program/top/README | 5 ++--- src/program/top/top.lua | 18 ++++++++---------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/core/app.lua b/src/core/app.lua index 352f5999f3..fa251390bb 100644 --- a/src/core/app.lua +++ b/src/core/app.lua @@ -175,7 +175,7 @@ function apply_config_actions (actions, conf) function ops.start (name) local class = conf.apps[name].class local arg = conf.apps[name].arg - local shmpath, shmorig = "apps/"..name, shm.path + local shmpath, shmorig = "counters/"..name, shm.path shm.path = shmpath local app = class:new(arg) shm.path = shmorig @@ -530,11 +530,11 @@ function selftest () config.app(c_counter, "App4", App4) configure(c_counter) main({done = function () return app_table.App4.test_counter end}) - assert(S.stat(shm.root.."/"..shm.resolve("apps/App4/test")), - "Missing : apps/App4/test") + assert(S.stat(shm.root.."/"..shm.resolve("counters/App4/test")), + "Missing : counters/App4/test") configure(config.new()) - assert(not S.stat(shm.root.."/"..shm.resolve("apps/App4")), - "Failed to unlink apps/App4") + assert(not S.stat(shm.root.."/"..shm.resolve("counters/App4")), + "Failed to unlink counters/App4") print("OK") end diff --git a/src/program/top/README b/src/program/top/README index ae91f6e4c4..ce815d6f89 100644 --- a/src/program/top/README +++ b/src/program/top/README @@ -3,9 +3,8 @@ Usage: -h, --help Print usage information. - -a, --app - -l, --link - Print counters of app or link by and exit. + -c, --counters + Print counters of object by and exit. Display realtime performance statistics for a running Snabb instance with . If is not supplied and there is only one Snabb instance, top will diff --git a/src/program/top/top.lua b/src/program/top/top.lua index 3b1f57a600..d69a999695 100644 --- a/src/program/top/top.lua +++ b/src/program/top/top.lua @@ -12,26 +12,23 @@ local histogram = require("core.histogram") local usage = require("program.top.README_inc") local long_opts = { - help = "h", app = "a", link = "l" + help = "h", counters = "c" } function clearterm () io.write('\027[2J') end function run (args) local opt = {} - local app_name = nil - local link_name = nil + local object = nil function opt.h (arg) print(usage) main.exit(1) end - function opt.a (arg) app_name = arg end - function opt.l (arg) link_name = arg end - args = lib.dogetopt(args, opt, "ha:l:", long_opts) + function opt.c (arg) object = arg end + args = lib.dogetopt(args, opt, "hc:", long_opts) if #args > 1 then print(usage) main.exit(1) end local target_pid = select_snabb_instance(args[1]) - if app_name then list_counters("//"..target_pid.."/apps/"..app_name) - elseif link_name then list_counters("//"..target_pid.."/counters/"..link_name) - else top(target_pid) end + if object then list_counters(target_pid, object) + else top(target_pid) end ordered_exit(0) end @@ -58,7 +55,8 @@ function ordered_exit (value) os.exit(value) end -function list_counters (path) +function list_counters (pid, object) + local path = "//"..pid.."/counters/"..object local cnames = shm.children(path) table.sort(cnames, function (a, b) return a < b end) for _, cname in ipairs(cnames) do From 924ff4e6e82880d296d6be9ddae4dda3aef4dff0 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Mon, 18 Apr 2016 19:30:15 +0200 Subject: [PATCH 10/39] lib.json: Import JSON4Lua 1.0.0, include encode functionality. --- src/lib/json.lua | 658 ++++++++++++++++++++++++++++++----------------- 1 file changed, 417 insertions(+), 241 deletions(-) diff --git a/src/lib/json.lua b/src/lib/json.lua index 86cbf85e43..9ab4abd38e 100644 --- a/src/lib/json.lua +++ b/src/lib/json.lua @@ -1,241 +1,417 @@ --- JSON4Lua: JSON encoding / decoding support for the Lua language. --- json Module. --- Author: Craig Mason-Jones --- Homepage: http://json.luaforge.net/ --- Version: 0.9.40 --- This module is released under the MIT License (MIT). --- --- NOTE: This is only the decode functionality ripped out from JSON4Lua. --- See: https://github.com/craigmj/json4lua - -module(..., package.seeall) - -local math = require('math') -local string = require("string") -local table = require("table") - -local base = _G - --- Private functions -local decode_scanArray -local decode_scanComment -local decode_scanConstant -local decode_scanNumber -local decode_scanObject -local decode_scanString -local decode_scanWhitespace - ---- Decodes a JSON string and returns the decoded value as a Lua data structure / value. --- @param s The string to scan. --- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. --- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, --- and the position of the first character after --- the scanned JSON object. -function decode(s, startPos) - startPos = startPos and startPos or 1 - startPos = decode_scanWhitespace(s,startPos) - base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']') - local curChar = string.sub(s,startPos,startPos) - -- Object - if curChar=='{' then - return decode_scanObject(s,startPos) - end - -- Array - if curChar=='[' then - return decode_scanArray(s,startPos) - end - -- Number - if string.find("+-0123456789.e", curChar, 1, true) then - return decode_scanNumber(s,startPos) - end - -- String - if curChar==[["]] or curChar==[[']] then - return decode_scanString(s,startPos) - end - if string.sub(s,startPos,startPos+1)=='/*' then - return decode(s, decode_scanComment(s,startPos)) - end - -- Otherwise, it must be a constant - return decode_scanConstant(s,startPos) -end - ---- The null function allows one to specify a null value in an associative array (which is otherwise --- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } -function null() - return null -- so json.null() will also return null ;-) -end ------------------------------------------------------------------------------ --- Internal, PRIVATE functions. --- Following a Python-like convention, I have prefixed all these 'PRIVATE' --- functions with an underscore. ------------------------------------------------------------------------------ - ---- Scans an array from JSON into a Lua object --- startPos begins at the start of the array. --- Returns the array and the next starting position --- @param s The string being scanned. --- @param startPos The starting position for the scan. --- @return table, int The scanned array as a table, and the position of the next character to scan. -function decode_scanArray(s,startPos) - local array = {} -- The return value - local stringLen = string.len(s) - base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s ) - startPos = startPos + 1 - -- Infinite loop for array elements - repeat - startPos = decode_scanWhitespace(s,startPos) - base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.') - local curChar = string.sub(s,startPos,startPos) - if (curChar==']') then - return array, startPos+1 - end - if (curChar==',') then - startPos = decode_scanWhitespace(s,startPos+1) - end - base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.') - object, startPos = decode(s,startPos) - table.insert(array,object) - until false -end - ---- Scans a comment and discards the comment. --- Returns the position of the next character following the comment. --- @param string s The JSON string to scan. --- @param int startPos The starting position of the comment -function decode_scanComment(s, startPos) - base.assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos) - local endPos = string.find(s,'*/',startPos+2) - base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos) - return endPos+2 -end - ---- Scans for given constants: true, false or null --- Returns the appropriate Lua type, and the position of the next character to read. --- @param s The string being scanned. --- @param startPos The position in the string at which to start scanning. --- @return object, int The object (true, false or nil) and the position at which the next character should be --- scanned. -function decode_scanConstant(s, startPos) - local consts = { ["true"] = true, ["false"] = false, ["null"] = nil } - local constNames = {"true","false","null"} - - for i,k in base.pairs(constNames) do - --print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k) - if string.sub(s,startPos, startPos + string.len(k) -1 )==k then - return consts[k], startPos + string.len(k) - end - end - base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos) -end - ---- Scans a number from the JSON encoded string. --- (in fact, also is able to scan numeric +- eqns, which is not --- in the JSON spec.) --- Returns the number, and the position of the next character --- after the number. --- @param s The string being scanned. --- @param startPos The position at which to start scanning. --- @return number, int The extracted number and the position of the next character to scan. -function decode_scanNumber(s,startPos) - local endPos = startPos+1 - local stringLen = string.len(s) - local acceptableChars = "+-0123456789.e" - while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true) - and endPos<=stringLen - ) do - endPos = endPos + 1 - end - local stringValue = 'return ' .. string.sub(s,startPos, endPos-1) - local stringEval = base.loadstring(stringValue) - base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos) - return stringEval(), endPos -end - ---- Scans a JSON object into a Lua object. --- startPos begins at the start of the object. --- Returns the object and the next starting position. --- @param s The string being scanned. --- @param startPos The starting position of the scan. --- @return table, int The scanned object as a table and the position of the next character to scan. -function decode_scanObject(s,startPos) - local object = {} - local stringLen = string.len(s) - local key, value - base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s) - startPos = startPos + 1 - repeat - startPos = decode_scanWhitespace(s,startPos) - base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.') - local curChar = string.sub(s,startPos,startPos) - if (curChar=='}') then - return object,startPos+1 - end - if (curChar==',') then - startPos = decode_scanWhitespace(s,startPos+1) - end - base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.') - -- Scan the key - key, startPos = decode(s,startPos) - base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) - startPos = decode_scanWhitespace(s,startPos) - base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) - base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos) - startPos = decode_scanWhitespace(s,startPos+1) - base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) - value, startPos = decode(s,startPos) - object[key]=value - until false -- infinite loop while key-value pairs are found -end - ---- Scans a JSON string from the opening inverted comma or single quote to the --- end of the string. --- Returns the string extracted as a Lua string, --- and the position of the next non-string character --- (after the closing inverted comma or single quote). --- @param s The string being scanned. --- @param startPos The starting position of the scan. --- @return string, int The extracted string as a Lua string, and the next character to parse. -function decode_scanString(s,startPos) - base.assert(startPos, 'decode_scanString(..) called without start position') - local startChar = string.sub(s,startPos,startPos) - base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string') - local escaped = false - local endPos = startPos + 1 - local bEnded = false - local stringLen = string.len(s) - repeat - local curChar = string.sub(s,endPos,endPos) - -- Character escaping is only used to escape the string delimiters - if not escaped then - if curChar==[[\]] then - escaped = true - else - bEnded = curChar==startChar - end - else - -- If we're escaped, we accept the current character come what may - escaped = false - end - endPos = endPos + 1 - base.assert(endPos <= stringLen+1, "String decoding failed: unterminated string at position " .. endPos) - until bEnded - local stringValue = 'return ' .. string.sub(s, startPos, endPos-1) - local stringEval = base.loadstring(stringValue) - base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos) - return stringEval(), endPos -end - ---- Scans a JSON string skipping all whitespace from the current start position. --- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. --- @param s The string being scanned --- @param startPos The starting position where we should begin removing whitespace. --- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string --- was reached. -function decode_scanWhitespace(s,startPos) - local whitespace=" \n\r\t" - local stringLen = string.len(s) - while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do - startPos = startPos + 1 - end - return startPos -end +----------------------------------------------------------------------------- +-- JSON4Lua: JSON encoding / decoding support for the Lua language. +-- json Module. +-- Author: Craig Mason-Jones +-- Homepage: http://github.com/craigmj/json4lua/ +-- Version: 1.0.0 +-- This module is released under the MIT License (MIT). +-- Please see LICENCE.txt for details. +-- +-- USAGE: +-- This module exposes two functions: +-- json.encode(o) +-- Returns the table / string / boolean / number / nil / json.null value as a JSON-encoded string. +-- json.decode(json_string) +-- Returns a Lua object populated with the data encoded in the JSON string json_string. +-- +-- REQUIREMENTS: +-- compat-5.1 if using Lua 5.0 +-- +-- CHANGELOG +-- 0.9.20 Introduction of local Lua functions for private functions (removed _ function prefix). +-- Fixed Lua 5.1 compatibility issues. +-- Introduced json.null to have null values in associative arrays. +-- json.encode() performance improvement (more than 50%) through table.concat rather than .. +-- Introduced decode ability to ignore /**/ comments in the JSON string. +-- 0.9.10 Fix to array encoding / decoding to correctly manage nil/null values in arrays. +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Imports and dependencies +----------------------------------------------------------------------------- +local math = require('math') +local string = require("string") +local table = require("table") + +----------------------------------------------------------------------------- +-- Module declaration +----------------------------------------------------------------------------- +local json = {} -- Public namespace +local json_private = {} -- Private namespace + +-- Public functions + +-- Private functions +local decode_scanArray +local decode_scanComment +local decode_scanConstant +local decode_scanNumber +local decode_scanObject +local decode_scanString +local decode_scanWhitespace +local encodeString +local isArray +local isEncodable + +----------------------------------------------------------------------------- +-- PUBLIC FUNCTIONS +----------------------------------------------------------------------------- +--- Encodes an arbitrary Lua object / variable. +-- @param v The Lua object / variable to be JSON encoded. +-- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) +function json.encode (v) + -- Handle nil values + if v==nil then + return "null" + end + + local vtype = type(v) + + -- Handle strings + if vtype=='string' then + return '"' .. json_private.encodeString(v) .. '"' -- Need to handle encoding in string + end + + -- Handle booleans + if vtype=='number' or vtype=='boolean' then + return tostring(v) + end + + -- Handle tables + if vtype=='table' then + local rval = {} + -- Consider arrays separately + local bArray, maxCount = isArray(v) + if bArray then + for i = 1,maxCount do + table.insert(rval, json.encode(v[i])) + end + else -- An object, not an array + for i,j in pairs(v) do + if isEncodable(i) and isEncodable(j) then + table.insert(rval, '"' .. json_private.encodeString(i) .. '":' .. json.encode(j)) + end + end + end + if bArray then + return '[' .. table.concat(rval,',') ..']' + else + return '{' .. table.concat(rval,',') .. '}' + end + end + + -- Handle null values + if vtype=='function' and v==null then + return 'null' + end + + assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. tostring(v)) +end + + +--- Decodes a JSON string and returns the decoded value as a Lua data structure / value. +-- @param s The string to scan. +-- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. +-- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, +-- and the position of the first character after +-- the scanned JSON object. +function json.decode(s, startPos) + startPos = startPos and startPos or 1 + startPos = decode_scanWhitespace(s,startPos) + assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']') + local curChar = string.sub(s,startPos,startPos) + -- Object + if curChar=='{' then + return decode_scanObject(s,startPos) + end + -- Array + if curChar=='[' then + return decode_scanArray(s,startPos) + end + -- Number + if string.find("+-0123456789.e", curChar, 1, true) then + return decode_scanNumber(s,startPos) + end + -- String + if curChar==[["]] or curChar==[[']] then + return decode_scanString(s,startPos) + end + if string.sub(s,startPos,startPos+1)=='/*' then + return decode(s, decode_scanComment(s,startPos)) + end + -- Otherwise, it must be a constant + return decode_scanConstant(s,startPos) +end + +--- The null function allows one to specify a null value in an associative array (which is otherwise +-- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } +function null() + return null -- so json.null() will also return null ;-) +end +----------------------------------------------------------------------------- +-- Internal, PRIVATE functions. +-- Following a Python-like convention, I have prefixed all these 'PRIVATE' +-- functions with an underscore. +----------------------------------------------------------------------------- + +--- Scans an array from JSON into a Lua object +-- startPos begins at the start of the array. +-- Returns the array and the next starting position +-- @param s The string being scanned. +-- @param startPos The starting position for the scan. +-- @return table, int The scanned array as a table, and the position of the next character to scan. +function decode_scanArray(s,startPos) + local array = {} -- The return value + local stringLen = string.len(s) + assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s ) + startPos = startPos + 1 + -- Infinite loop for array elements + repeat + startPos = decode_scanWhitespace(s,startPos) + assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.') + local curChar = string.sub(s,startPos,startPos) + if (curChar==']') then + return array, startPos+1 + end + if (curChar==',') then + startPos = decode_scanWhitespace(s,startPos+1) + end + assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.') + object, startPos = json.decode(s,startPos) + table.insert(array,object) + until false +end + +--- Scans a comment and discards the comment. +-- Returns the position of the next character following the comment. +-- @param string s The JSON string to scan. +-- @param int startPos The starting position of the comment +function decode_scanComment(s, startPos) + assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos) + local endPos = string.find(s,'*/',startPos+2) + assert(endPos~=nil, "Unterminated comment in string at " .. startPos) + return endPos+2 +end + +--- Scans for given constants: true, false or null +-- Returns the appropriate Lua type, and the position of the next character to read. +-- @param s The string being scanned. +-- @param startPos The position in the string at which to start scanning. +-- @return object, int The object (true, false or nil) and the position at which the next character should be +-- scanned. +function decode_scanConstant(s, startPos) + local consts = { ["true"] = true, ["false"] = false, ["null"] = nil } + local constNames = {"true","false","null"} + + for i,k in pairs(constNames) do + if string.sub(s,startPos, startPos + string.len(k) -1 )==k then + return consts[k], startPos + string.len(k) + end + end + assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos) +end + +--- Scans a number from the JSON encoded string. +-- (in fact, also is able to scan numeric +- eqns, which is not +-- in the JSON spec.) +-- Returns the number, and the position of the next character +-- after the number. +-- @param s The string being scanned. +-- @param startPos The position at which to start scanning. +-- @return number, int The extracted number and the position of the next character to scan. +function decode_scanNumber(s,startPos) + local endPos = startPos+1 + local stringLen = string.len(s) + local acceptableChars = "+-0123456789.e" + while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true) + and endPos<=stringLen + ) do + endPos = endPos + 1 + end + local stringValue = 'return ' .. string.sub(s,startPos, endPos-1) + local stringEval = loadstring(stringValue) + assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos) + return stringEval(), endPos +end + +--- Scans a JSON object into a Lua object. +-- startPos begins at the start of the object. +-- Returns the object and the next starting position. +-- @param s The string being scanned. +-- @param startPos The starting position of the scan. +-- @return table, int The scanned object as a table and the position of the next character to scan. +function decode_scanObject(s,startPos) + local object = {} + local stringLen = string.len(s) + local key, value + assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s) + startPos = startPos + 1 + repeat + startPos = decode_scanWhitespace(s,startPos) + assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.') + local curChar = string.sub(s,startPos,startPos) + if (curChar=='}') then + return object,startPos+1 + end + if (curChar==',') then + startPos = decode_scanWhitespace(s,startPos+1) + end + assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.') + -- Scan the key + key, startPos = json.decode(s,startPos) + assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + startPos = decode_scanWhitespace(s,startPos) + assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos) + startPos = decode_scanWhitespace(s,startPos+1) + assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + value, startPos = json.decode(s,startPos) + object[key]=value + until false -- infinite loop while key-value pairs are found +end + +-- START SoniEx2 +-- Initialize some things used by decode_scanString +-- You know, for efficiency +local escapeSequences = { + ["\\t"] = "\t", + ["\\f"] = "\f", + ["\\r"] = "\r", + ["\\n"] = "\n", + ["\\b"] = "\b" +} +setmetatable(escapeSequences, {__index = function(t,k) + -- skip "\" aka strip escape + return string.sub(k,2) +end}) +-- END SoniEx2 + +--- Scans a JSON string from the opening inverted comma or single quote to the +-- end of the string. +-- Returns the string extracted as a Lua string, +-- and the position of the next non-string character +-- (after the closing inverted comma or single quote). +-- @param s The string being scanned. +-- @param startPos The starting position of the scan. +-- @return string, int The extracted string as a Lua string, and the next character to parse. +function decode_scanString(s,startPos) + assert(startPos, 'decode_scanString(..) called without start position') + local startChar = string.sub(s,startPos,startPos) + -- START SoniEx2 + -- PS: I don't think single quotes are valid JSON + assert(startChar == [["]] or startChar == [[']],'decode_scanString called for a non-string') + --assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) + local t = {} + local i,j = startPos,startPos + while string.find(s, startChar, j+1) ~= j+1 do + local oldj = j + i,j = string.find(s, "\\.", j+1) + local x,y = string.find(s, startChar, oldj+1) + if not i or x < i then + i,j = x,y-1 + end + table.insert(t, string.sub(s, oldj+1, i-1)) + if string.sub(s, i, j) == "\\u" then + local a = string.sub(s,j+1,j+4) + j = j + 4 + local n = tonumber(a, 16) + assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) + -- math.floor(x/2^y) == lazy right shift + -- a % 2^b == bitwise_and(a, (2^b)-1) + -- 64 = 2^6 + -- 4096 = 2^12 (or 2^6 * 2^6) + local x + if n < 0x80 then + x = string.char(n % 0x80) + elseif n < 0x800 then + -- [110x xxxx] [10xx xxxx] + x = string.char(0xC0 + (math.floor(n/64) % 0x20), 0x80 + (n % 0x40)) + else + -- [1110 xxxx] [10xx xxxx] [10xx xxxx] + x = string.char(0xE0 + (math.floor(n/4096) % 0x10), 0x80 + (math.floor(n/64) % 0x40), 0x80 + (n % 0x40)) + end + table.insert(t, x) + else + table.insert(t, escapeSequences[string.sub(s, i, j)]) + end + end + table.insert(t,string.sub(j, j+1)) + assert(string.find(s, startChar, j+1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")") + return table.concat(t,""), j+2 + -- END SoniEx2 +end + +--- Scans a JSON string skipping all whitespace from the current start position. +-- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. +-- @param s The string being scanned +-- @param startPos The starting position where we should begin removing whitespace. +-- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string +-- was reached. +function decode_scanWhitespace(s,startPos) + local whitespace=" \n\r\t" + local stringLen = string.len(s) + while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do + startPos = startPos + 1 + end + return startPos +end + +--- Encodes a string to be JSON-compatible. +-- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) +-- @param s The string to return as a JSON encoded (i.e. backquoted string) +-- @return The string appropriately escaped. + +local escapeList = { + ['"'] = '\\"', + ['\\'] = '\\\\', + ['/'] = '\\/', + ['\b'] = '\\b', + ['\f'] = '\\f', + ['\n'] = '\\n', + ['\r'] = '\\r', + ['\t'] = '\\t' +} + +function json_private.encodeString(s) + local s = tostring(s) + return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat +end + +-- Determines whether the given Lua type is an array or a table / dictionary. +-- We consider any table an array if it has indexes 1..n for its n items, and no +-- other data in the table. +-- I think this method is currently a little 'flaky', but can't think of a good way around it yet... +-- @param t The table to evaluate as an array +-- @return boolean, number True if the table can be represented as an array, false otherwise. If true, +-- the second returned value is the maximum +-- number of indexed elements in the array. +function isArray(t) + -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable + -- (with the possible exception of 'n') + local maxIndex = 0 + for k,v in pairs(t) do + if (type(k)=='number' and math.floor(k)==k and 1<=k) then -- k,v is an indexed pair + if (not isEncodable(v)) then return false end -- All array elements must be encodable + maxIndex = math.max(maxIndex,k) + else + if (k=='n') then + if v ~= table.getn(t) then return false end -- False if n does not hold the number of elements + else -- Else of (k=='n') + if isEncodable(v) then return false end + end -- End of (k~='n') + end -- End of k,v not an indexed pair + end -- End of loop across all pairs + return true, maxIndex +end + +--- Determines whether the given Lua object / table / variable can be JSON encoded. The only +-- types that are JSON encodable are: string, boolean, number, nil, table and json.null. +-- In this implementation, all other types are ignored. +-- @param o The object to examine. +-- @return boolean True if the object should be JSON encoded, false if it should be ignored. +function isEncodable(o) + local t = type(o) + return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null) +end + +return json \ No newline at end of file From 8e34093991bb1c4579c84fd0569b54cafc762889 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Mon, 18 Apr 2016 21:05:00 +0200 Subject: [PATCH 11/39] lib.macaddress: Support numeric initialization; add method to get numeric representation. --- src/apps/intel/intel_app.lua | 4 ++-- src/lib/macaddress.lua | 26 +++++++++++++++++--------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index f104fad553..a4224cf0d0 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -64,7 +64,7 @@ function Intel82599:new (arg) if conf.vmdq then self.counters['phys-address'] = counter.open('phys-address') counter.set(self.counters['phys-address'], - macaddress:new(conf.macaddr).bits) + macaddress:new(conf.macaddr):int()) end return self @@ -102,7 +102,7 @@ function Intel82599:reconfig(arg) if conf.vmdq then counter.set(self.counters['phys-address'], - macaddress:new(conf.macaddr).bits) + macaddress:new(conf.macaddr):int()) end end diff --git a/src/lib/macaddress.lua b/src/lib/macaddress.lua index e003fa1cc7..e55bdcfb3c 100644 --- a/src/lib/macaddress.lua +++ b/src/lib/macaddress.lua @@ -13,17 +13,21 @@ function mac_mt:new (m) return m end local macobj = mac_t() - local i = 0; - for b in m:gmatch('%x%x') do - if i == 6 then - -- avoid out of bound array index + if type(m) == 'string' then + local i = 0; + for b in m:gmatch('%x%x') do + if i == 6 then + -- avoid out of bound array index + return nil, "malformed MAC address: " .. m + end + macobj.bytes[i] = tonumber(b, 16) + i = i + 1 + end + if i < 6 then return nil, "malformed MAC address: " .. m end - macobj.bytes[i] = tonumber(b, 16) - i = i + 1 - end - if i < 6 then - return nil, "malformed MAC address: " .. m + else + macobj.bits = m end return macobj end @@ -38,6 +42,10 @@ function mac_mt.__eq (a, b) return a.bits == b.bits end +function mac_mt:int () + return self.bits +end + function mac_mt:subbits (i,j) local b = bit.rshift(self.bits, i) local mask = bit.bnot(bit.lshift(0xffffffffffffLL, j-i)) From 5f9efd280f683e394c677e6d606b2d4624831d5f Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Mon, 18 Apr 2016 21:05:34 +0200 Subject: [PATCH 12/39] =?UTF-8?q?core.link:=20Create=20=E2=80=9Cdiscontinu?= =?UTF-8?q?ity-time=E2=80=9D=20counters.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/link.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/link.lua b/src/core/link.lua index ed825e2606..34e2ffc942 100644 --- a/src/core/link.lua +++ b/src/core/link.lua @@ -28,6 +28,8 @@ function new (name) for _, c in ipairs(counternames) do r.stats[c] = counter.open("counters/"..name.."/"..c) end + counter.set(counter.open("counters/"..name.."/discontinuity-time"), + C.get_unix_time()) return r end @@ -35,6 +37,7 @@ function free (r, name) for _, c in ipairs(counternames) do counter.delete("counters/"..name.."/"..c) end + counter.delete("counters/"..name.."/discontinuity-time") shm.unmap(r) shm.unlink("links/"..name) end From 7b391482949cf10caac34871bb5890af80ab2f6b Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Tue, 19 Apr 2016 17:44:30 +0200 Subject: [PATCH 13/39] snabb top: add `--yang' option to print YANG model as JSON. --- src/program/top/README | 2 + src/program/top/top.lua | 109 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/program/top/README b/src/program/top/README index ce815d6f89..9fa3500804 100644 --- a/src/program/top/README +++ b/src/program/top/README @@ -5,6 +5,8 @@ Usage: Print usage information. -c, --counters Print counters of object by and exit. + -y, --yang + Print YANG model encoded in JSON and exit. Display realtime performance statistics for a running Snabb instance with . If is not supplied and there is only one Snabb instance, top will diff --git a/src/program/top/top.lua b/src/program/top/top.lua index d69a999695..34ae80be6e 100644 --- a/src/program/top/top.lua +++ b/src/program/top/top.lua @@ -9,10 +9,12 @@ local shm = require("core.shm") local counter = require("core.counter") local S = require("syscall") local histogram = require("core.histogram") +local json = require("lib.json") +local macaddress = require("lib.macaddress") local usage = require("program.top.README_inc") local long_opts = { - help = "h", counters = "c" + help = "h", counters = "c", yang = "y" } function clearterm () io.write('\027[2J') end @@ -20,15 +22,18 @@ function clearterm () io.write('\027[2J') end function run (args) local opt = {} local object = nil + local yang = false function opt.h (arg) print(usage) main.exit(1) end function opt.c (arg) object = arg end - args = lib.dogetopt(args, opt, "hc:", long_opts) + function opt.y () yang = true end + args = lib.dogetopt(args, opt, "hc:y", long_opts) if #args > 1 then print(usage) main.exit(1) end local target_pid = select_snabb_instance(args[1]) - if object then list_counters(target_pid, object) - else top(target_pid) end + if object then list_counters(target_pid, object) + elseif yang then dump_yang(target_pid) + else top(target_pid) end ordered_exit(0) end @@ -55,18 +60,106 @@ function ordered_exit (value) os.exit(value) end +function read_counter (name, path) + if path then name = path.."/"..name end + local value = counter.read(counter.open(name, 'readonly')) + counter.delete(name) + return value +end + function list_counters (pid, object) local path = "//"..pid.."/counters/"..object local cnames = shm.children(path) table.sort(cnames, function (a, b) return a < b end) for _, cname in ipairs(cnames) do - local cpath = path.."/"..cname - local value = counter.read(counter.open(cpath, 'readonly')) - print_row({30, 30}, {cname, lib.comma_value(value)}) - counter.delete(cpath) + print_row({30, 30}, {cname, lib.comma_value(read_counter(cname, path))}) end end +function dump_yang (instance_pid) + local instance_tree = "//"..instance_pid + local interface_state = {} + local types = { [0x1000] = 'hardware', + [0x1001] = 'virtual', + [0x1002] = 'link' } + + for _, link in ipairs(shm.children(instance_tree.."/links")) do + local counters = instance_tree.."/counters/"..link + local statistics = {} + statistics['discontinuity-time'] = + totime(read_counter('discontinuity-time', counters)) + statistics['in-octets'] = + tohex64(read_counter('rxbytes', counters)) + statistics['out-octets'] = + tohex64(read_counter('txbytes', counters)) + statistics['out-discards'] = + truncate32(tonumber(read_counter('txdrop', counters))) + table.insert(interface_state, { name = link, + type = types[0x1002], + statistics = statistics }) + end + + for _, name in ipairs(shm.children(instance_tree.."/counters")) do + local counters = instance_tree.."/counters/"..name + local exists = {} + for _, c in ipairs(shm.children(counters)) do exists[c] = true end + local type = nil + if exists['type'] then + type = types[tonumber(read_counter('type', counters))] + end + if type then + local statistics = {} + statistics['discontinuity-time'] = + totime(read_counter('discontinuity-time', counters)) + for _, c in ipairs({'in-octets', 'in-unicast', + 'in-broadcast', 'in-multicast', + 'out-octets', 'out-unicast', + 'out-broadcast', 'out-multicast'}) do + if exists[c] then + statistics[c] = tohex64(read_counter(c, counters)) + end + end + for _, c in ipairs({'in-discards', 'out-discards'}) do + if exists[c] then + statistics[c] = truncate32(read_counter(c, counters)) + end + end + local interface = { name = name, type = type, statistics = statistics} + if exists['phys-address'] then + interface['phys-address'] = + tomac(read_counter('phys-address', counters)) + end + table.insert(interface_state, interface) + end + end + + print(json.encode({['interface-state'] = interface_state})) +end + +function tomac (n) + return tostring(macaddress:new(n)) +end + +function totime (n) + return os.date("!%FT%TZ", tonumber(n)) +end + +function truncate32 (n) + local box = ffi.new("union { uint64_t i64; uint32_t i32[2]; }") + box.i64 = n + if ffi.abi("le") then return tonumber(box.i32[0]) + elseif ffi.abi("be") then return tonumber(box.i32[1]) end +end + +function tohex64 (n) + local box = ffi.new("uint64_t[1]") + box[0] = n + local s = ffi.string(box, 8) + if ffi.abi("le") then s = s:reverse() end + return string.format("0x%02X%02X%02X%02X%02X%02X%02X%02X", s:byte(1, 8)) +end + + function top (instance_pid) local instance_tree = "//"..instance_pid local counters = open_counters(instance_tree) From 898474106031398f46b7e90dfe7cfd53403c661b Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 20 Apr 2016 13:43:36 +0200 Subject: [PATCH 14/39] snabb top --yang: Represent uint64_t as decimal string. --- src/program/top/top.lua | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/program/top/top.lua b/src/program/top/top.lua index 34ae80be6e..317c0a2a77 100644 --- a/src/program/top/top.lua +++ b/src/program/top/top.lua @@ -89,9 +89,9 @@ function dump_yang (instance_pid) statistics['discontinuity-time'] = totime(read_counter('discontinuity-time', counters)) statistics['in-octets'] = - tohex64(read_counter('rxbytes', counters)) + tostring64(read_counter('rxbytes', counters)) statistics['out-octets'] = - tohex64(read_counter('txbytes', counters)) + tostring64(read_counter('txbytes', counters)) statistics['out-discards'] = truncate32(tonumber(read_counter('txdrop', counters))) table.insert(interface_state, { name = link, @@ -116,7 +116,7 @@ function dump_yang (instance_pid) 'out-octets', 'out-unicast', 'out-broadcast', 'out-multicast'}) do if exists[c] then - statistics[c] = tohex64(read_counter(c, counters)) + statistics[c] = tostring64(read_counter(c, counters)) end end for _, c in ipairs({'in-discards', 'out-discards'}) do @@ -151,12 +151,10 @@ function truncate32 (n) elseif ffi.abi("be") then return tonumber(box.i32[1]) end end -function tohex64 (n) - local box = ffi.new("uint64_t[1]") - box[0] = n - local s = ffi.string(box, 8) - if ffi.abi("le") then s = s:reverse() end - return string.format("0x%02X%02X%02X%02X%02X%02X%02X%02X", s:byte(1, 8)) +function tostring64 (n) + assert(ffi.istype('uint64_t', n)) + local s = tostring(n) + return s:sub(1, #s - 3) end From ee00d167b2aa437861f5ee04804e80755053ef36 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 28 Apr 2016 18:26:55 +0200 Subject: [PATCH 15/39] [core.lib] Generalize `timer' to optionally accept 'repeating' option and support injecting a function to determine the current time. --- src/README.md | 14 +++++++++++--- src/core/lib.lua | 21 +++++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/README.md b/src/README.md index b406eeabd9..4cfbb73dd7 100644 --- a/src/README.md +++ b/src/README.md @@ -543,10 +543,18 @@ Returns a table that acts as a bounds checked wrapper around a C array of ctype and the caller must ensure that the allocated memory region at *base*/*offset* is at least `sizeof(type)*size` bytes long. -— Function **lib.timer** *s* +— Function **lib.timer** *duration*, *mode*, *timefun* + +Returns a closure that will return `false` until *duration* has elapsed. If +*mode* is `'repeating'` the timer will reset itself after returning `true`, +thus implementing an interval timer. *Timefun* is used to get a monotonic time. +*Timefun* defaults to `C.get_time_ns`. + +The “deadline” for a given *duration* is computed by adding *duration* to the +result of calling *timefun*, and is saved in the resulting closure. A +*duration* has elapsed when its deadline is less than or equal the value +obtained using *timefun* when calling the closure. -Returns a function that accepts no parameters and acts as a predicate to -test if *ns* nanoseconds have elapsed. — Function **lib.waitfor** *condition* diff --git a/src/core/lib.lua b/src/core/lib.lua index b6223cc8e6..6e37a90964 100644 --- a/src/core/lib.lua +++ b/src/core/lib.lua @@ -246,10 +246,23 @@ function bounds_checked (type, base, offset, size) return wrap(ffi.cast(tptr, ffi.cast("uint8_t *", base) + offset)) end --- Return a function that will return false until NS nanoseconds have elapsed. -function timer (ns) - local deadline = C.get_time_ns() + ns - return function () return C.get_time_ns() >= deadline end +-- Return a function that will return false until duration has elapsed. +-- If mode is 'repeating' the timer will reset itself after returning true, +-- thus implementing an interval timer. Timefun defaults to `C.get_time_ns'. +function timer (duration, mode, timefun) + timefun = timefun or C.get_time_ns + local deadline = timefun() + duration + local function oneshot () + return timefun() >= deadline + end + local function repeating () + if timefun() >= deadline then + deadline = deadline + duration + return true + else return false end + end + if mode == 'repeating' then return repeating + else return oneshot end end -- Loop until the function `condition` returns true. From 45490b8e162c96e16f739672c951d9b92f896fd5 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 28 Apr 2016 19:30:55 +0200 Subject: [PATCH 16/39] Revert "Intel_app: Add RFC 7223 app counters." This reverts commit 8bb3215bf759f6c53af07c5c13afd2ea60003334. --- src/apps/intel/intel_app.lua | 45 ++++-------------------------------- 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index a4224cf0d0..80cace3d9f 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -6,10 +6,8 @@ local zone = require("jit.zone") local basic_apps = require("apps.basic.basic_apps") local ffi = require("ffi") local lib = require("core.lib") -local counter = require("core.counter") local pci = require("lib.hardware.pci") local register = require("lib.hardware.register") -local macaddress = require("lib.macaddress") local intel10g = require("apps.intel.intel10g") local freelist = require("core.freelist") local receive, transmit, full, empty = link.receive, link.transmit, link.full, link.empty @@ -38,7 +36,6 @@ end -- Create an Intel82599 App for the device with 'pciaddress'. function Intel82599:new (arg) local conf = config.parse_app_arg(arg) - local self = setmetatable({counters = {}}, Intel82599) if conf.vmdq then if devices[conf.pciaddr] == nil then @@ -48,26 +45,12 @@ function Intel82599:new (arg) local poolnum = firsthole(dev.vflist)-1 local vf = dev.pf:new_vf(poolnum) dev.vflist[poolnum+1] = vf - self.dev = vf:open(conf) + return setmetatable({dev=vf:open(conf)}, Intel82599) else - self.dev = assert(intel10g.new_sf(conf):open(), "Can not open device.") - self.zone = "intel" + local dev = intel10g.new_sf(conf):open() + if not dev then return null end + return setmetatable({dev=dev, zone="intel"}, Intel82599) end - - self.counters['type'] = counter.open('type') - self.counters['discontinuity-time'] = counter.open('discontinuity-time') - self.counters['in-octets'] = counter.open('in-octets') - self.counters['in-discards'] = counter.open('in-discards') - self.counters['out-octets'] = counter.open('out-octets') - counter.set(self.counters['type'], 0x1000) -- Hardware interface - counter.set(self.counters['discontinuity-time'], C.get_unix_time()) - if conf.vmdq then - self.counters['phys-address'] = counter.open('phys-address') - counter.set(self.counters['phys-address'], - macaddress:new(conf.macaddr):int()) - end - - return self end function Intel82599:stop() @@ -88,9 +71,6 @@ function Intel82599:stop() if close_pf then close_pf:close() end - - -- delete counters - for name, _ in pairs(self.counters) do counter.delete(name) end end @@ -99,11 +79,6 @@ function Intel82599:reconfig(arg) assert((not not self.dev.pf) == (not not conf.vmdq), "Can't reconfig from VMDQ to single-port or viceversa") self.dev:reconfig(conf) - - if conf.vmdq then - counter.set(self.counters['phys-address'], - macaddress:new(conf.macaddr):int()) - end end -- Allocate receive buffers from the given freelist. @@ -121,13 +96,6 @@ function Intel82599:pull () transmit(l, self.dev:receive()) end self:add_receive_buffers() - if self.dev.rxstats then - counter.set(self.counters['in-octets'], - bit.lshift(self.dev.pf.qs.QBRC_H[self.dev.rxstats]()+0LL, 32) - + self.dev.pf.qs.QBRC_L[self.dev.rxstats]()) - counter.set(self.counters['in-discards'], - self.dev.pf.qs.QPRDC[self.dev.rxstats]()) - end end function Intel82599:add_receive_buffers () @@ -148,11 +116,6 @@ function Intel82599:push () end end self.dev:sync_transmit() - if self.dev.txstats then - counter.set(self.counters['out-octets'], - bit.lshift(self.dev.pf.qs.QBTC_H[self.dev.txstats]()+0LL, 32) - + self.dev.pf.qs.QBTC_L[self.dev.txstats]()) - end end -- Report on relevant status and statistics. From f0ed10b70781505e38809acd189744067993f516 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 28 Apr 2016 20:34:23 +0200 Subject: [PATCH 17/39] intel_app: expose per-pciaddress statistics in `counters/'. --- src/apps/intel/intel_app.lua | 69 +++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 80cace3d9f..0fff2dfc5e 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -6,6 +6,8 @@ local zone = require("jit.zone") local basic_apps = require("apps.basic.basic_apps") local ffi = require("ffi") local lib = require("core.lib") +local shm = require("core.shm") +local counter = require("core.counter") local pci = require("lib.hardware.pci") local register = require("lib.hardware.register") local intel10g = require("apps.intel.intel10g") @@ -36,21 +38,49 @@ end -- Create an Intel82599 App for the device with 'pciaddress'. function Intel82599:new (arg) local conf = config.parse_app_arg(arg) + local self = {} if conf.vmdq then if devices[conf.pciaddr] == nil then - devices[conf.pciaddr] = {pf=intel10g.new_pf(conf):open(), vflist={}} + local pf = intel10g.new_pf(conf):open() + devices[conf.pciaddr] = {pf=pf, vflist={}, stats={register=pf.s}} end local dev = devices[conf.pciaddr] local poolnum = firsthole(dev.vflist)-1 local vf = dev.pf:new_vf(poolnum) dev.vflist[poolnum+1] = vf - return setmetatable({dev=vf:open(conf)}, Intel82599) + self.dev = vf:open(conf) + self.stats = devices[conf.pciaddr].stats else - local dev = intel10g.new_sf(conf):open() - if not dev then return null end - return setmetatable({dev=dev, zone="intel"}, Intel82599) + self.dev = assert(intel10g.new_sf(conf):open(), "Can not open device.") + self.stats = { register = self.dev.s } + self.zone = "intel" end + if not self.stats.counters then + local counters = {} + local path = "/counters/"..conf.pciaddr.."/" + counters['type'] = counter.open(path..'type') + counters['discontinuity-time'] = counter.open(path..'discontinuity-time') + counters['in-octets'] = counter.open(path..'in-octets') + counters['in-multicast'] = counter.open(path..'in-multicast') + counters['in-broadcast'] = counter.open(path..'in-broadcast') + counters['in-discards'] = counter.open(path..'in-discards') + counters['out-octets'] = counter.open(path..'out-octets') + counters['out-multicast'] = counter.open(path..'out-multicast') + counters['out-broadcast'] = counter.open(path..'out-broadcast') + counters['out-discards'] = counter.open(path..'out-discards') + counter.set(counters['type'], 0x1000) -- Hardware interface + counter.set(counters['discontinuity-time'], C.get_unix_time()) + if not conf.vmdq and conf.macaddr then + counters['phys-address'] = counter.open(path..'phys-address') + counter.set(counters['phys-address'], + macaddress:new(conf.macaddr):int()) + end + self.stats.counters = counters + self.stats.path = path + self.stats.sync_timer = lib.timer(0.001, 'repeating', engine.now) + end + return setmetatable(self, Intel82599) end function Intel82599:stop() @@ -71,6 +101,12 @@ function Intel82599:stop() if close_pf then close_pf:close() end + if not self.dev.pf or close_pf then + for name, _ in pairs(self.stats.counters) do + counter.delete(self.stats.path..name) + end + shm.unlink(self.stats.path) + end end @@ -79,6 +115,11 @@ function Intel82599:reconfig(arg) assert((not not self.dev.pf) == (not not conf.vmdq), "Can't reconfig from VMDQ to single-port or viceversa") self.dev:reconfig(conf) + + if not self.dev.pf and conf.macaddr then + counter.set(self.stats.counters['phys-address'], + macaddress:new(conf.macaddr):int()) + end end -- Allocate receive buffers from the given freelist. @@ -96,6 +137,9 @@ function Intel82599:pull () transmit(l, self.dev:receive()) end self:add_receive_buffers() + if self.stats.sync_timer() then + self:sync_stats() + end end function Intel82599:add_receive_buffers () @@ -105,6 +149,21 @@ function Intel82599:add_receive_buffers () end end +-- Synchronize self.stats.register a and self.stats.counters. +function Intel82599:sync_stats () + --- XXX - it is questionable if I choose the right register to counter + --- mapping + local counters, register = self.stats.counters, self.stats.register + counter.set(counters['in-octets'], register.GORC64()) + counter.set(counters['in-multicast'], register.MPRC()) + counter.set(counters['in-broadcast'], register.BPRC()) + counter.set(counters['in-discards'], register.TPR() - register.GPRC()) + counter.set(counters['out-octets'], register.GOTC64()) + counter.set(counters['out-multicast'], register.MPTC()) + counter.set(counters['out-broadcast'], register.BPTC()) + counter.set(counters['out-discards'], register.TPT() - register.GPTC()) +end + -- Push packets from our 'rx' link onto the network. function Intel82599:push () local l = self.input.rx From c186591e312fb90c4c2d847be01255fb7dba0c8b Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Mon, 25 Apr 2016 16:25:06 +0200 Subject: [PATCH 18/39] lib.protocol.ethernet: Add n_mcast, branch-free Multicast predicate. --- src/apps/vhost/vhost_user.lua | 16 ++++++---------- src/lib/protocol/README.md | 9 +++++++++ src/lib/protocol/ethernet.lua | 7 ++++++- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/apps/vhost/vhost_user.lua b/src/apps/vhost/vhost_user.lua index 96e82780eb..16936709bf 100644 --- a/src/apps/vhost/vhost_user.lua +++ b/src/apps/vhost/vhost_user.lua @@ -102,20 +102,16 @@ end function VhostUser:tx_callback (p) counter.add(self.counters['out-octets'], packet.length(p)) - if ethernet:is_mcast(packet.data(p)) then - counter.add(self.counters['out-multicast']) - else - counter.add(self.counters['out-unicast']) - end + local mcast = ethernet:n_mcast(packet.data(p)) + counter.add(self.counters['out-multicast'], mcast) + counter.add(self.counters['out-unicast'], 1 - mcast) end function VhostUser:rx_callback (p) counter.add(self.counters['in-octets'], packet.length(p)) - if ethernet:is_mcast(packet.data(p)) then - counter.add(self.counters['in-multicast']) - else - counter.add(self.counters['in-unicast']) - end + local mcast = ethernet:n_mcast(packet.data(p)) + counter.add(self.counters['in-multicast'], mcast) + counter.add(self.counters['in-unicast'], 1 - mcast) end function VhostUser:rxdrop_callback (p) diff --git a/src/lib/protocol/README.md b/src/lib/protocol/README.md index 6f239687d7..4a13483a4b 100644 --- a/src/lib/protocol/README.md +++ b/src/lib/protocol/README.md @@ -98,6 +98,15 @@ Returns the binary representation of MAC address denoted by *string*. Returns the string representation of *mac* address. +— Function **ethernet:is_mcast** *mac* + +Returns a true value if *mac* address denotes a [Multicast address](https://en.wikipedia.org/wiki/Multicast_address#Ethernet). + +— Function **ethernet:n_mcast** *mac* + +Returns 1 if *mac* address denotes a [Multicast address](https://en.wikipedia.org/wiki/Multicast_address#Ethernet) +and 0 otherwise. + — Function **ethernet:ipv6_mcast** *ip* Returns the MAC address for IPv6 multicast *ip* as defined by RFC2464, diff --git a/src/lib/protocol/ethernet.lua b/src/lib/protocol/ethernet.lua index 0a55c20f1b..b5abed8d31 100644 --- a/src/lib/protocol/ethernet.lua +++ b/src/lib/protocol/ethernet.lua @@ -76,9 +76,14 @@ function ethernet:ipv6_mcast(ip) return result end +-- Return 1 if MAC address has its group bit set and 0 otherwise +function ethernet:n_mcast (addr) + return band(addr[0], 0x01) +end + -- Check whether a MAC address has its group bit set function ethernet:is_mcast (addr) - return band(addr[0], 0x01) ~= 0 + return ethernet:n_mcast(addr) ~= 0 end -- Instance methods From b09e843465686fd1afad4c6c48a248f33de6b07e Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Fri, 27 May 2016 15:24:11 +0200 Subject: [PATCH 19/39] Fix for f0ed10b: require macaddress module. --- src/apps/intel/intel_app.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index f5773d66f3..2645e2d22c 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -10,6 +10,7 @@ local shm = require("core.shm") local counter = require("core.counter") local pci = require("lib.hardware.pci") local register = require("lib.hardware.register") +local macaddress = require("lib.macaddress") local intel10g = require("apps.intel.intel10g") local receive, transmit, full, empty = link.receive, link.transmit, link.full, link.empty Intel82599 = {} From 62e2fbfe631a56631f7c115fd73b82550256fd83 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 1 Jun 2016 15:25:44 +0200 Subject: [PATCH 20/39] Revert "lib.json: Import JSON4Lua 1.0.0, include encode functionality." This reverts commit 924ff4e6e82880d296d6be9ddae4dda3aef4dff0. --- src/lib/json.lua | 658 +++++++++++++++++------------------------------ 1 file changed, 241 insertions(+), 417 deletions(-) diff --git a/src/lib/json.lua b/src/lib/json.lua index 9ab4abd38e..86cbf85e43 100644 --- a/src/lib/json.lua +++ b/src/lib/json.lua @@ -1,417 +1,241 @@ ------------------------------------------------------------------------------ --- JSON4Lua: JSON encoding / decoding support for the Lua language. --- json Module. --- Author: Craig Mason-Jones --- Homepage: http://github.com/craigmj/json4lua/ --- Version: 1.0.0 --- This module is released under the MIT License (MIT). --- Please see LICENCE.txt for details. --- --- USAGE: --- This module exposes two functions: --- json.encode(o) --- Returns the table / string / boolean / number / nil / json.null value as a JSON-encoded string. --- json.decode(json_string) --- Returns a Lua object populated with the data encoded in the JSON string json_string. --- --- REQUIREMENTS: --- compat-5.1 if using Lua 5.0 --- --- CHANGELOG --- 0.9.20 Introduction of local Lua functions for private functions (removed _ function prefix). --- Fixed Lua 5.1 compatibility issues. --- Introduced json.null to have null values in associative arrays. --- json.encode() performance improvement (more than 50%) through table.concat rather than .. --- Introduced decode ability to ignore /**/ comments in the JSON string. --- 0.9.10 Fix to array encoding / decoding to correctly manage nil/null values in arrays. ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ --- Imports and dependencies ------------------------------------------------------------------------------ -local math = require('math') -local string = require("string") -local table = require("table") - ------------------------------------------------------------------------------ --- Module declaration ------------------------------------------------------------------------------ -local json = {} -- Public namespace -local json_private = {} -- Private namespace - --- Public functions - --- Private functions -local decode_scanArray -local decode_scanComment -local decode_scanConstant -local decode_scanNumber -local decode_scanObject -local decode_scanString -local decode_scanWhitespace -local encodeString -local isArray -local isEncodable - ------------------------------------------------------------------------------ --- PUBLIC FUNCTIONS ------------------------------------------------------------------------------ ---- Encodes an arbitrary Lua object / variable. --- @param v The Lua object / variable to be JSON encoded. --- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) -function json.encode (v) - -- Handle nil values - if v==nil then - return "null" - end - - local vtype = type(v) - - -- Handle strings - if vtype=='string' then - return '"' .. json_private.encodeString(v) .. '"' -- Need to handle encoding in string - end - - -- Handle booleans - if vtype=='number' or vtype=='boolean' then - return tostring(v) - end - - -- Handle tables - if vtype=='table' then - local rval = {} - -- Consider arrays separately - local bArray, maxCount = isArray(v) - if bArray then - for i = 1,maxCount do - table.insert(rval, json.encode(v[i])) - end - else -- An object, not an array - for i,j in pairs(v) do - if isEncodable(i) and isEncodable(j) then - table.insert(rval, '"' .. json_private.encodeString(i) .. '":' .. json.encode(j)) - end - end - end - if bArray then - return '[' .. table.concat(rval,',') ..']' - else - return '{' .. table.concat(rval,',') .. '}' - end - end - - -- Handle null values - if vtype=='function' and v==null then - return 'null' - end - - assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. tostring(v)) -end - - ---- Decodes a JSON string and returns the decoded value as a Lua data structure / value. --- @param s The string to scan. --- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. --- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, --- and the position of the first character after --- the scanned JSON object. -function json.decode(s, startPos) - startPos = startPos and startPos or 1 - startPos = decode_scanWhitespace(s,startPos) - assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']') - local curChar = string.sub(s,startPos,startPos) - -- Object - if curChar=='{' then - return decode_scanObject(s,startPos) - end - -- Array - if curChar=='[' then - return decode_scanArray(s,startPos) - end - -- Number - if string.find("+-0123456789.e", curChar, 1, true) then - return decode_scanNumber(s,startPos) - end - -- String - if curChar==[["]] or curChar==[[']] then - return decode_scanString(s,startPos) - end - if string.sub(s,startPos,startPos+1)=='/*' then - return decode(s, decode_scanComment(s,startPos)) - end - -- Otherwise, it must be a constant - return decode_scanConstant(s,startPos) -end - ---- The null function allows one to specify a null value in an associative array (which is otherwise --- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } -function null() - return null -- so json.null() will also return null ;-) -end ------------------------------------------------------------------------------ --- Internal, PRIVATE functions. --- Following a Python-like convention, I have prefixed all these 'PRIVATE' --- functions with an underscore. ------------------------------------------------------------------------------ - ---- Scans an array from JSON into a Lua object --- startPos begins at the start of the array. --- Returns the array and the next starting position --- @param s The string being scanned. --- @param startPos The starting position for the scan. --- @return table, int The scanned array as a table, and the position of the next character to scan. -function decode_scanArray(s,startPos) - local array = {} -- The return value - local stringLen = string.len(s) - assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s ) - startPos = startPos + 1 - -- Infinite loop for array elements - repeat - startPos = decode_scanWhitespace(s,startPos) - assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.') - local curChar = string.sub(s,startPos,startPos) - if (curChar==']') then - return array, startPos+1 - end - if (curChar==',') then - startPos = decode_scanWhitespace(s,startPos+1) - end - assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.') - object, startPos = json.decode(s,startPos) - table.insert(array,object) - until false -end - ---- Scans a comment and discards the comment. --- Returns the position of the next character following the comment. --- @param string s The JSON string to scan. --- @param int startPos The starting position of the comment -function decode_scanComment(s, startPos) - assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos) - local endPos = string.find(s,'*/',startPos+2) - assert(endPos~=nil, "Unterminated comment in string at " .. startPos) - return endPos+2 -end - ---- Scans for given constants: true, false or null --- Returns the appropriate Lua type, and the position of the next character to read. --- @param s The string being scanned. --- @param startPos The position in the string at which to start scanning. --- @return object, int The object (true, false or nil) and the position at which the next character should be --- scanned. -function decode_scanConstant(s, startPos) - local consts = { ["true"] = true, ["false"] = false, ["null"] = nil } - local constNames = {"true","false","null"} - - for i,k in pairs(constNames) do - if string.sub(s,startPos, startPos + string.len(k) -1 )==k then - return consts[k], startPos + string.len(k) - end - end - assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos) -end - ---- Scans a number from the JSON encoded string. --- (in fact, also is able to scan numeric +- eqns, which is not --- in the JSON spec.) --- Returns the number, and the position of the next character --- after the number. --- @param s The string being scanned. --- @param startPos The position at which to start scanning. --- @return number, int The extracted number and the position of the next character to scan. -function decode_scanNumber(s,startPos) - local endPos = startPos+1 - local stringLen = string.len(s) - local acceptableChars = "+-0123456789.e" - while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true) - and endPos<=stringLen - ) do - endPos = endPos + 1 - end - local stringValue = 'return ' .. string.sub(s,startPos, endPos-1) - local stringEval = loadstring(stringValue) - assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos) - return stringEval(), endPos -end - ---- Scans a JSON object into a Lua object. --- startPos begins at the start of the object. --- Returns the object and the next starting position. --- @param s The string being scanned. --- @param startPos The starting position of the scan. --- @return table, int The scanned object as a table and the position of the next character to scan. -function decode_scanObject(s,startPos) - local object = {} - local stringLen = string.len(s) - local key, value - assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s) - startPos = startPos + 1 - repeat - startPos = decode_scanWhitespace(s,startPos) - assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.') - local curChar = string.sub(s,startPos,startPos) - if (curChar=='}') then - return object,startPos+1 - end - if (curChar==',') then - startPos = decode_scanWhitespace(s,startPos+1) - end - assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.') - -- Scan the key - key, startPos = json.decode(s,startPos) - assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) - startPos = decode_scanWhitespace(s,startPos) - assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) - assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos) - startPos = decode_scanWhitespace(s,startPos+1) - assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) - value, startPos = json.decode(s,startPos) - object[key]=value - until false -- infinite loop while key-value pairs are found -end - --- START SoniEx2 --- Initialize some things used by decode_scanString --- You know, for efficiency -local escapeSequences = { - ["\\t"] = "\t", - ["\\f"] = "\f", - ["\\r"] = "\r", - ["\\n"] = "\n", - ["\\b"] = "\b" -} -setmetatable(escapeSequences, {__index = function(t,k) - -- skip "\" aka strip escape - return string.sub(k,2) -end}) --- END SoniEx2 - ---- Scans a JSON string from the opening inverted comma or single quote to the --- end of the string. --- Returns the string extracted as a Lua string, --- and the position of the next non-string character --- (after the closing inverted comma or single quote). --- @param s The string being scanned. --- @param startPos The starting position of the scan. --- @return string, int The extracted string as a Lua string, and the next character to parse. -function decode_scanString(s,startPos) - assert(startPos, 'decode_scanString(..) called without start position') - local startChar = string.sub(s,startPos,startPos) - -- START SoniEx2 - -- PS: I don't think single quotes are valid JSON - assert(startChar == [["]] or startChar == [[']],'decode_scanString called for a non-string') - --assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) - local t = {} - local i,j = startPos,startPos - while string.find(s, startChar, j+1) ~= j+1 do - local oldj = j - i,j = string.find(s, "\\.", j+1) - local x,y = string.find(s, startChar, oldj+1) - if not i or x < i then - i,j = x,y-1 - end - table.insert(t, string.sub(s, oldj+1, i-1)) - if string.sub(s, i, j) == "\\u" then - local a = string.sub(s,j+1,j+4) - j = j + 4 - local n = tonumber(a, 16) - assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) - -- math.floor(x/2^y) == lazy right shift - -- a % 2^b == bitwise_and(a, (2^b)-1) - -- 64 = 2^6 - -- 4096 = 2^12 (or 2^6 * 2^6) - local x - if n < 0x80 then - x = string.char(n % 0x80) - elseif n < 0x800 then - -- [110x xxxx] [10xx xxxx] - x = string.char(0xC0 + (math.floor(n/64) % 0x20), 0x80 + (n % 0x40)) - else - -- [1110 xxxx] [10xx xxxx] [10xx xxxx] - x = string.char(0xE0 + (math.floor(n/4096) % 0x10), 0x80 + (math.floor(n/64) % 0x40), 0x80 + (n % 0x40)) - end - table.insert(t, x) - else - table.insert(t, escapeSequences[string.sub(s, i, j)]) - end - end - table.insert(t,string.sub(j, j+1)) - assert(string.find(s, startChar, j+1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")") - return table.concat(t,""), j+2 - -- END SoniEx2 -end - ---- Scans a JSON string skipping all whitespace from the current start position. --- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. --- @param s The string being scanned --- @param startPos The starting position where we should begin removing whitespace. --- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string --- was reached. -function decode_scanWhitespace(s,startPos) - local whitespace=" \n\r\t" - local stringLen = string.len(s) - while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do - startPos = startPos + 1 - end - return startPos -end - ---- Encodes a string to be JSON-compatible. --- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) --- @param s The string to return as a JSON encoded (i.e. backquoted string) --- @return The string appropriately escaped. - -local escapeList = { - ['"'] = '\\"', - ['\\'] = '\\\\', - ['/'] = '\\/', - ['\b'] = '\\b', - ['\f'] = '\\f', - ['\n'] = '\\n', - ['\r'] = '\\r', - ['\t'] = '\\t' -} - -function json_private.encodeString(s) - local s = tostring(s) - return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat -end - --- Determines whether the given Lua type is an array or a table / dictionary. --- We consider any table an array if it has indexes 1..n for its n items, and no --- other data in the table. --- I think this method is currently a little 'flaky', but can't think of a good way around it yet... --- @param t The table to evaluate as an array --- @return boolean, number True if the table can be represented as an array, false otherwise. If true, --- the second returned value is the maximum --- number of indexed elements in the array. -function isArray(t) - -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable - -- (with the possible exception of 'n') - local maxIndex = 0 - for k,v in pairs(t) do - if (type(k)=='number' and math.floor(k)==k and 1<=k) then -- k,v is an indexed pair - if (not isEncodable(v)) then return false end -- All array elements must be encodable - maxIndex = math.max(maxIndex,k) - else - if (k=='n') then - if v ~= table.getn(t) then return false end -- False if n does not hold the number of elements - else -- Else of (k=='n') - if isEncodable(v) then return false end - end -- End of (k~='n') - end -- End of k,v not an indexed pair - end -- End of loop across all pairs - return true, maxIndex -end - ---- Determines whether the given Lua object / table / variable can be JSON encoded. The only --- types that are JSON encodable are: string, boolean, number, nil, table and json.null. --- In this implementation, all other types are ignored. --- @param o The object to examine. --- @return boolean True if the object should be JSON encoded, false if it should be ignored. -function isEncodable(o) - local t = type(o) - return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null) -end - -return json \ No newline at end of file +-- JSON4Lua: JSON encoding / decoding support for the Lua language. +-- json Module. +-- Author: Craig Mason-Jones +-- Homepage: http://json.luaforge.net/ +-- Version: 0.9.40 +-- This module is released under the MIT License (MIT). +-- +-- NOTE: This is only the decode functionality ripped out from JSON4Lua. +-- See: https://github.com/craigmj/json4lua + +module(..., package.seeall) + +local math = require('math') +local string = require("string") +local table = require("table") + +local base = _G + +-- Private functions +local decode_scanArray +local decode_scanComment +local decode_scanConstant +local decode_scanNumber +local decode_scanObject +local decode_scanString +local decode_scanWhitespace + +--- Decodes a JSON string and returns the decoded value as a Lua data structure / value. +-- @param s The string to scan. +-- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. +-- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, +-- and the position of the first character after +-- the scanned JSON object. +function decode(s, startPos) + startPos = startPos and startPos or 1 + startPos = decode_scanWhitespace(s,startPos) + base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']') + local curChar = string.sub(s,startPos,startPos) + -- Object + if curChar=='{' then + return decode_scanObject(s,startPos) + end + -- Array + if curChar=='[' then + return decode_scanArray(s,startPos) + end + -- Number + if string.find("+-0123456789.e", curChar, 1, true) then + return decode_scanNumber(s,startPos) + end + -- String + if curChar==[["]] or curChar==[[']] then + return decode_scanString(s,startPos) + end + if string.sub(s,startPos,startPos+1)=='/*' then + return decode(s, decode_scanComment(s,startPos)) + end + -- Otherwise, it must be a constant + return decode_scanConstant(s,startPos) +end + +--- The null function allows one to specify a null value in an associative array (which is otherwise +-- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } +function null() + return null -- so json.null() will also return null ;-) +end +----------------------------------------------------------------------------- +-- Internal, PRIVATE functions. +-- Following a Python-like convention, I have prefixed all these 'PRIVATE' +-- functions with an underscore. +----------------------------------------------------------------------------- + +--- Scans an array from JSON into a Lua object +-- startPos begins at the start of the array. +-- Returns the array and the next starting position +-- @param s The string being scanned. +-- @param startPos The starting position for the scan. +-- @return table, int The scanned array as a table, and the position of the next character to scan. +function decode_scanArray(s,startPos) + local array = {} -- The return value + local stringLen = string.len(s) + base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s ) + startPos = startPos + 1 + -- Infinite loop for array elements + repeat + startPos = decode_scanWhitespace(s,startPos) + base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.') + local curChar = string.sub(s,startPos,startPos) + if (curChar==']') then + return array, startPos+1 + end + if (curChar==',') then + startPos = decode_scanWhitespace(s,startPos+1) + end + base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.') + object, startPos = decode(s,startPos) + table.insert(array,object) + until false +end + +--- Scans a comment and discards the comment. +-- Returns the position of the next character following the comment. +-- @param string s The JSON string to scan. +-- @param int startPos The starting position of the comment +function decode_scanComment(s, startPos) + base.assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos) + local endPos = string.find(s,'*/',startPos+2) + base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos) + return endPos+2 +end + +--- Scans for given constants: true, false or null +-- Returns the appropriate Lua type, and the position of the next character to read. +-- @param s The string being scanned. +-- @param startPos The position in the string at which to start scanning. +-- @return object, int The object (true, false or nil) and the position at which the next character should be +-- scanned. +function decode_scanConstant(s, startPos) + local consts = { ["true"] = true, ["false"] = false, ["null"] = nil } + local constNames = {"true","false","null"} + + for i,k in base.pairs(constNames) do + --print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k) + if string.sub(s,startPos, startPos + string.len(k) -1 )==k then + return consts[k], startPos + string.len(k) + end + end + base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos) +end + +--- Scans a number from the JSON encoded string. +-- (in fact, also is able to scan numeric +- eqns, which is not +-- in the JSON spec.) +-- Returns the number, and the position of the next character +-- after the number. +-- @param s The string being scanned. +-- @param startPos The position at which to start scanning. +-- @return number, int The extracted number and the position of the next character to scan. +function decode_scanNumber(s,startPos) + local endPos = startPos+1 + local stringLen = string.len(s) + local acceptableChars = "+-0123456789.e" + while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true) + and endPos<=stringLen + ) do + endPos = endPos + 1 + end + local stringValue = 'return ' .. string.sub(s,startPos, endPos-1) + local stringEval = base.loadstring(stringValue) + base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos) + return stringEval(), endPos +end + +--- Scans a JSON object into a Lua object. +-- startPos begins at the start of the object. +-- Returns the object and the next starting position. +-- @param s The string being scanned. +-- @param startPos The starting position of the scan. +-- @return table, int The scanned object as a table and the position of the next character to scan. +function decode_scanObject(s,startPos) + local object = {} + local stringLen = string.len(s) + local key, value + base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s) + startPos = startPos + 1 + repeat + startPos = decode_scanWhitespace(s,startPos) + base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.') + local curChar = string.sub(s,startPos,startPos) + if (curChar=='}') then + return object,startPos+1 + end + if (curChar==',') then + startPos = decode_scanWhitespace(s,startPos+1) + end + base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.') + -- Scan the key + key, startPos = decode(s,startPos) + base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + startPos = decode_scanWhitespace(s,startPos) + base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos) + startPos = decode_scanWhitespace(s,startPos+1) + base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + value, startPos = decode(s,startPos) + object[key]=value + until false -- infinite loop while key-value pairs are found +end + +--- Scans a JSON string from the opening inverted comma or single quote to the +-- end of the string. +-- Returns the string extracted as a Lua string, +-- and the position of the next non-string character +-- (after the closing inverted comma or single quote). +-- @param s The string being scanned. +-- @param startPos The starting position of the scan. +-- @return string, int The extracted string as a Lua string, and the next character to parse. +function decode_scanString(s,startPos) + base.assert(startPos, 'decode_scanString(..) called without start position') + local startChar = string.sub(s,startPos,startPos) + base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string') + local escaped = false + local endPos = startPos + 1 + local bEnded = false + local stringLen = string.len(s) + repeat + local curChar = string.sub(s,endPos,endPos) + -- Character escaping is only used to escape the string delimiters + if not escaped then + if curChar==[[\]] then + escaped = true + else + bEnded = curChar==startChar + end + else + -- If we're escaped, we accept the current character come what may + escaped = false + end + endPos = endPos + 1 + base.assert(endPos <= stringLen+1, "String decoding failed: unterminated string at position " .. endPos) + until bEnded + local stringValue = 'return ' .. string.sub(s, startPos, endPos-1) + local stringEval = base.loadstring(stringValue) + base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos) + return stringEval(), endPos +end + +--- Scans a JSON string skipping all whitespace from the current start position. +-- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. +-- @param s The string being scanned +-- @param startPos The starting position where we should begin removing whitespace. +-- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string +-- was reached. +function decode_scanWhitespace(s,startPos) + local whitespace=" \n\r\t" + local stringLen = string.len(s) + while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do + startPos = startPos + 1 + end + return startPos +end From f4834a515b5eef30de76dca2e572b77b28fd625f Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 1 Jun 2016 15:26:23 +0200 Subject: [PATCH 21/39] snabb top: revert --yang dump. --- src/program/top/README | 2 - src/program/top/top.lua | 91 +---------------------------------------- 2 files changed, 2 insertions(+), 91 deletions(-) diff --git a/src/program/top/README b/src/program/top/README index 9fa3500804..ce815d6f89 100644 --- a/src/program/top/README +++ b/src/program/top/README @@ -5,8 +5,6 @@ Usage: Print usage information. -c, --counters Print counters of object by and exit. - -y, --yang - Print YANG model encoded in JSON and exit. Display realtime performance statistics for a running Snabb instance with . If is not supplied and there is only one Snabb instance, top will diff --git a/src/program/top/top.lua b/src/program/top/top.lua index 317c0a2a77..8334d76be3 100644 --- a/src/program/top/top.lua +++ b/src/program/top/top.lua @@ -9,12 +9,10 @@ local shm = require("core.shm") local counter = require("core.counter") local S = require("syscall") local histogram = require("core.histogram") -local json = require("lib.json") -local macaddress = require("lib.macaddress") local usage = require("program.top.README_inc") local long_opts = { - help = "h", counters = "c", yang = "y" + help = "h", counters = "c" } function clearterm () io.write('\027[2J') end @@ -22,17 +20,14 @@ function clearterm () io.write('\027[2J') end function run (args) local opt = {} local object = nil - local yang = false function opt.h (arg) print(usage) main.exit(1) end function opt.c (arg) object = arg end - function opt.y () yang = true end - args = lib.dogetopt(args, opt, "hc:y", long_opts) + args = lib.dogetopt(args, opt, "hc:", long_opts) if #args > 1 then print(usage) main.exit(1) end local target_pid = select_snabb_instance(args[1]) if object then list_counters(target_pid, object) - elseif yang then dump_yang(target_pid) else top(target_pid) end ordered_exit(0) end @@ -76,88 +71,6 @@ function list_counters (pid, object) end end -function dump_yang (instance_pid) - local instance_tree = "//"..instance_pid - local interface_state = {} - local types = { [0x1000] = 'hardware', - [0x1001] = 'virtual', - [0x1002] = 'link' } - - for _, link in ipairs(shm.children(instance_tree.."/links")) do - local counters = instance_tree.."/counters/"..link - local statistics = {} - statistics['discontinuity-time'] = - totime(read_counter('discontinuity-time', counters)) - statistics['in-octets'] = - tostring64(read_counter('rxbytes', counters)) - statistics['out-octets'] = - tostring64(read_counter('txbytes', counters)) - statistics['out-discards'] = - truncate32(tonumber(read_counter('txdrop', counters))) - table.insert(interface_state, { name = link, - type = types[0x1002], - statistics = statistics }) - end - - for _, name in ipairs(shm.children(instance_tree.."/counters")) do - local counters = instance_tree.."/counters/"..name - local exists = {} - for _, c in ipairs(shm.children(counters)) do exists[c] = true end - local type = nil - if exists['type'] then - type = types[tonumber(read_counter('type', counters))] - end - if type then - local statistics = {} - statistics['discontinuity-time'] = - totime(read_counter('discontinuity-time', counters)) - for _, c in ipairs({'in-octets', 'in-unicast', - 'in-broadcast', 'in-multicast', - 'out-octets', 'out-unicast', - 'out-broadcast', 'out-multicast'}) do - if exists[c] then - statistics[c] = tostring64(read_counter(c, counters)) - end - end - for _, c in ipairs({'in-discards', 'out-discards'}) do - if exists[c] then - statistics[c] = truncate32(read_counter(c, counters)) - end - end - local interface = { name = name, type = type, statistics = statistics} - if exists['phys-address'] then - interface['phys-address'] = - tomac(read_counter('phys-address', counters)) - end - table.insert(interface_state, interface) - end - end - - print(json.encode({['interface-state'] = interface_state})) -end - -function tomac (n) - return tostring(macaddress:new(n)) -end - -function totime (n) - return os.date("!%FT%TZ", tonumber(n)) -end - -function truncate32 (n) - local box = ffi.new("union { uint64_t i64; uint32_t i32[2]; }") - box.i64 = n - if ffi.abi("le") then return tonumber(box.i32[0]) - elseif ffi.abi("be") then return tonumber(box.i32[1]) end -end - -function tostring64 (n) - assert(ffi.istype('uint64_t', n)) - local s = tostring(n) - return s:sub(1, #s - 3) -end - - function top (instance_pid) local instance_tree = "//"..instance_pid local counters = open_counters(instance_tree) From 793c394322294d8eb444244a5954315156392a37 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 2 Jun 2016 17:35:41 +0200 Subject: [PATCH 22/39] snabbify statistics model. --- model.txt | 41 ++++++++++++++++++++++++++ src/apps/intel/intel_app.lua | 54 ++++++++++++++++------------------- src/apps/vhost/vhost_user.lua | 30 ++++++++++--------- src/core/link.h | 2 +- src/core/link.lua | 15 +++++----- 5 files changed, 89 insertions(+), 53 deletions(-) create mode 100644 model.txt diff --git a/model.txt b/model.txt new file mode 100644 index 0000000000..79df1e9847 --- /dev/null +++ b/model.txt @@ -0,0 +1,41 @@ +RFC7223 Snabb +======= ====== +name +description? +type type (~= identity) +enabled? +link-up-down-trap-enable? +admin-status +oper-status status (= enum) +last-change? +if-index +phys-address? macaddr (uint64) +higher-layer-if* +lower-layer-if* +speed? = +discontinuity-time dtime (seconds since epoch) +in-octets? rxbytes +in-unicast-pkts? +in-broadcast-pkts? rxbcast +in-multicast-pkts? +in-discards? +in-errors? +in-unknown-protos? +out-octets? txbytes +out-unicast-pkts? +out-broadcast-pkts? txbcast +out-multicast-pkts? +out-discards? +out-errors? + + +snabb/type: + +0x1000 Hardware interface +0x1001 Virtual interface + +snabb/stats: + +(tx/rx) - bcast ⊆ mcast ⊆ packets + +(tx/rx) - drop (all dropped packets) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 2645e2d22c..7b30e66ac0 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -57,28 +57,22 @@ function Intel82599:new (arg) self.zone = "intel" end if not self.stats.counters then - local counters = {} - local path = "/counters/"..conf.pciaddr.."/" - counters['type'] = counter.open(path..'type') - counters['discontinuity-time'] = counter.open(path..'discontinuity-time') - counters['in-octets'] = counter.open(path..'in-octets') - counters['in-multicast'] = counter.open(path..'in-multicast') - counters['in-broadcast'] = counter.open(path..'in-broadcast') - counters['in-discards'] = counter.open(path..'in-discards') - counters['out-octets'] = counter.open(path..'out-octets') - counters['out-multicast'] = counter.open(path..'out-multicast') - counters['out-broadcast'] = counter.open(path..'out-broadcast') - counters['out-discards'] = counter.open(path..'out-discards') - counter.set(counters['type'], 0x1000) -- Hardware interface - counter.set(counters['discontinuity-time'], C.get_unix_time()) + self.stats.path = "/counters/"..conf.pciaddr.."/" + self.stats.sync_timer = lib.timer(0.001, 'repeating', engine.now) + self.stats.counters = {} + for _, name in ipairs( + {'type', 'dtime', + 'rxbytes', 'rxpackets', 'rxmcast', 'rxbcast', 'rxdrop', + 'txbytes', 'txpackets', 'txmcast', 'txbcast', 'txdrop'}) do + self.stats.counters[name] = counter.open(self.stats.path..name) + end + counter.set(self.stats.counters.type, 0x1000) -- Hardware interface + counter.set(self.stats.counters.dtime, C.get_unix_time()) if not conf.vmdq and conf.macaddr then - counters['phys-address'] = counter.open(path..'phys-address') - counter.set(counters['phys-address'], + self.stats.counters.macaddr = counter.open(self.stats.path..'macaddr') + counter.set(self.stats.counters.macaddr, macaddress:new(conf.macaddr):int()) end - self.stats.counters = counters - self.stats.path = path - self.stats.sync_timer = lib.timer(0.001, 'repeating', engine.now) end return setmetatable(self, Intel82599) end @@ -117,7 +111,7 @@ function Intel82599:reconfig(arg) self.dev:reconfig(conf) if not self.dev.pf and conf.macaddr then - counter.set(self.stats.counters['phys-address'], + counter.set(self.stats.counters.macaddr, macaddress:new(conf.macaddr):int()) end end @@ -151,17 +145,17 @@ end -- Synchronize self.stats.register a and self.stats.counters. function Intel82599:sync_stats () - --- XXX - it is questionable if I choose the right register to counter - --- mapping local counters, register = self.stats.counters, self.stats.register - counter.set(counters['in-octets'], register.GORC64()) - counter.set(counters['in-multicast'], register.MPRC()) - counter.set(counters['in-broadcast'], register.BPRC()) - counter.set(counters['in-discards'], register.TPR() - register.GPRC()) - counter.set(counters['out-octets'], register.GOTC64()) - counter.set(counters['out-multicast'], register.MPTC()) - counter.set(counters['out-broadcast'], register.BPTC()) - counter.set(counters['out-discards'], register.TPT() - register.GPTC()) + counter.set(counters.rxbytes, register.GORC64()) + counter.set(counters.rxpackets, register.GPRC()) + counter.set(counters.rxmcast, register.MPRC()) + counter.set(counters.rxbcast, register.BPRC()) + counter.set(counters.rxdrop, register.TPR() - register.GPRC()) + counter.set(counters.txbytes, register.GOTC64()) + counter.set(counters.txpackets, register.GPTC()) + counter.set(counters.txmcast, register.MPTC()) + counter.set(counters.txbcast, register.BPTC()) + counter.set(counters.txdrop, register.TPT() - register.GPTC()) end -- Push packets from our 'rx' link onto the network. diff --git a/src/apps/vhost/vhost_user.lua b/src/apps/vhost/vhost_user.lua index 16936709bf..fe2711769e 100644 --- a/src/apps/vhost/vhost_user.lua +++ b/src/apps/vhost/vhost_user.lua @@ -29,6 +29,12 @@ assert(ffi.sizeof("struct vhost_user_msg") == 276, "ABI error") VhostUser = {} +local provided_counters = { + 'type', 'dtime', + 'rxbytes', 'rxpackets', 'rxmcast', 'rxdrop', + 'txbytes', 'txpackets', 'txmcast' +} + function VhostUser:new (args) local o = { state = 'init', dev = nil, @@ -56,13 +62,11 @@ function VhostUser:new (args) end -- initialize counters self.counters = {} - for _, name in ipairs({'type', 'discontinuity-time', - 'in-octets', 'in-unicast', 'in-multicast', 'in-discards', - 'out-octets', 'out-unicast', 'out-multicast'}) do + for _, name in ipairs(provided_counters) do self.counters[name] = counter.open(name) end - counter.set(self.counters['type'], 0x1001) -- Virtual interface - counter.set(self.counters['discontinuity-time'], C.get_unix_time()) + counter.set(self.counters.type, 0x1001) -- Virtual interface + counter.set(self.counters.dtime, C.get_unix_time()) return self end @@ -101,21 +105,19 @@ function VhostUser:push () end function VhostUser:tx_callback (p) - counter.add(self.counters['out-octets'], packet.length(p)) - local mcast = ethernet:n_mcast(packet.data(p)) - counter.add(self.counters['out-multicast'], mcast) - counter.add(self.counters['out-unicast'], 1 - mcast) + counter.add(self.counters.txbytes, packet.length(p)) + counter.add(self.counters.txpackets) + counter.add(self.counters.txmcast, ethernet:n_mcast(packet.data(p))) end function VhostUser:rx_callback (p) - counter.add(self.counters['in-octets'], packet.length(p)) - local mcast = ethernet:n_mcast(packet.data(p)) - counter.add(self.counters['in-multicast'], mcast) - counter.add(self.counters['in-unicast'], 1 - mcast) + counter.add(self.counters.rxbytes, packet.length(p)) + counter.add(self.counters.rxpackets) + counter.add(self.counters.rxmcast, ethernet:n_mcast(packet.data(p))) end function VhostUser:rxdrop_callback (p) - counter.add(self.counters['in-discards']) + counter.add(self.counters.rxdrop) end -- Try to connect to QEMU. diff --git a/src/core/link.h b/src/core/link.h index c4ca2b45a5..f4caadaf91 100644 --- a/src/core/link.h +++ b/src/core/link.h @@ -9,7 +9,7 @@ struct link { // http://en.wikipedia.org/wiki/Circular_buffer struct packet *packets[LINK_RING_SIZE]; struct { - struct counter *txbytes, *rxbytes, *txpackets, *rxpackets, *txdrop; + struct counter *dtime, *txbytes, *rxbytes, *txpackets, *rxpackets, *txdrop; } stats; // Two cursors: // read: the next element to be read diff --git a/src/core/link.lua b/src/core/link.lua index 34e2ffc942..aacff1e741 100644 --- a/src/core/link.lua +++ b/src/core/link.lua @@ -21,23 +21,23 @@ local band = require("bit").band local size = C.LINK_RING_SIZE -- NB: Huge slow-down if this is not local max = C.LINK_MAX_PACKETS -local counternames = {"rxpackets", "txpackets", "rxbytes", "txbytes", "txdrop"} +local provided_counters = { + "dtime", "rxpackets", "rxbytes", "txpackets", "txbytes", "txdrop" +} function new (name) local r = shm.create("links/"..name, "struct link") - for _, c in ipairs(counternames) do + for _, c in ipairs(provided_counters) do r.stats[c] = counter.open("counters/"..name.."/"..c) end - counter.set(counter.open("counters/"..name.."/discontinuity-time"), - C.get_unix_time()) + counter.set(r.stats.dtime, C.get_unix_time()) return r end function free (r, name) - for _, c in ipairs(counternames) do + for _, c in ipairs(provided_counters) do counter.delete("counters/"..name.."/"..c) end - counter.delete("counters/"..name.."/discontinuity-time") shm.unmap(r) shm.unlink("links/"..name) end @@ -95,8 +95,7 @@ end function stats (r) local stats = {} - for _, c - in ipairs(counternames) do + for _, c in ipairs(provided_counters) do stats[c] = tonumber(counter.read(r.stats[c])) end return stats From 2b9ef2c82df610ec1470a5bb11615f5d8d0510bd Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 2 Jun 2016 17:37:13 +0200 Subject: [PATCH 23/39] intel_app: add ifTable MIB related counters. --- model.txt | 47 +++++++++++++----------- src/apps/intel/intel_app.lua | 69 ++++++++++++++++++++++++++---------- 2 files changed, 77 insertions(+), 39 deletions(-) diff --git a/model.txt b/model.txt index 79df1e9847..8f4e128823 100644 --- a/model.txt +++ b/model.txt @@ -1,41 +1,48 @@ -RFC7223 Snabb -======= ====== +RFC7223 Snabb ifTable MIB +======= ===== =========== name description? type type (~= identity) enabled? link-up-down-trap-enable? admin-status -oper-status status (= enum) +oper-status status (= enum) ifOperStatus last-change? if-index -phys-address? macaddr (uint64) +phys-address? macaddr (uint64) ifPhysAddress higher-layer-if* lower-layer-if* -speed? = -discontinuity-time dtime (seconds since epoch) -in-octets? rxbytes +speed? speed ifSpeed +discontinuity-time dtime (seconds since epoch) ifCounterDiscontinuityTime +in-octets? rxbytes ifInOctets + rxpackets in-unicast-pkts? -in-broadcast-pkts? rxbcast -in-multicast-pkts? -in-discards? -in-errors? +in-broadcast-pkts? rxbcast ifInBroadcastPkts + rxmcast +in-multicast-pkts? ifInMulticastPkts +in-discards? rxdrop ifInDiscards +in-errors? rxerrors ifInErrors in-unknown-protos? -out-octets? txbytes +out-octets? txbytes ifOutOctets + txpackets out-unicast-pkts? -out-broadcast-pkts? txbcast -out-multicast-pkts? -out-discards? -out-errors? - +out-broadcast-pkts? txbcast ifOutBroadcastPkts + txmcast +out-multicast-pkts? ifOutMulticastPkts +out-discards? txdrop ifOutDiscards +out-errors? txerrors ifOutErrors + rxpackets + txpackets + rxmcast + txmcast + mtu ifMtu + promisc ifPromiscuousMode snabb/type: 0x1000 Hardware interface 0x1001 Virtual interface -snabb/stats: +snabb/bcast, mcast, packets: (tx/rx) - bcast ⊆ mcast ⊆ packets - -(tx/rx) - drop (all dropped packets) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 7b30e66ac0..623fb1a3a3 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -34,6 +34,11 @@ local function firsthole(t) end end +local provided_counters = { + 'type', 'dtime', 'mtu', 'speed', 'status', 'promisc', 'macaddr', + 'rxbytes', 'rxpackets', 'rxmcast', 'rxbcast', 'rxdrop', 'rxerrors', + 'txbytes', 'txpackets', 'txmcast', 'txbcast', 'txdrop', 'txerrors' +} -- Create an Intel82599 App for the device with 'pciaddress'. function Intel82599:new (arg) @@ -43,7 +48,9 @@ function Intel82599:new (arg) if conf.vmdq then if devices[conf.pciaddr] == nil then local pf = intel10g.new_pf(conf):open() - devices[conf.pciaddr] = {pf=pf, vflist={}, stats={register=pf.s}} + devices[conf.pciaddr] = { pf = pf, + vflist = {}, + stats = { s = pf.s, r = pf.r, qs = pf.qs } } end local dev = devices[conf.pciaddr] local poolnum = firsthole(dev.vflist)-1 @@ -53,23 +60,22 @@ function Intel82599:new (arg) self.stats = devices[conf.pciaddr].stats else self.dev = assert(intel10g.new_sf(conf):open(), "Can not open device.") - self.stats = { register = self.dev.s } + self.stats = { s = self.dev.s, r = self.dev.r, qs = self.dev.qs } self.zone = "intel" end if not self.stats.counters then self.stats.path = "/counters/"..conf.pciaddr.."/" self.stats.sync_timer = lib.timer(0.001, 'repeating', engine.now) self.stats.counters = {} - for _, name in ipairs( - {'type', 'dtime', - 'rxbytes', 'rxpackets', 'rxmcast', 'rxbcast', 'rxdrop', - 'txbytes', 'txpackets', 'txmcast', 'txbcast', 'txdrop'}) do + for _, name in ipairs(provided_counters) do self.stats.counters[name] = counter.open(self.stats.path..name) end counter.set(self.stats.counters.type, 0x1000) -- Hardware interface counter.set(self.stats.counters.dtime, C.get_unix_time()) + counter.set(self.stats.counters.mtu, self.dev.mtu) + counter.set(self.stats.counters.speed, 10000000) -- 10 Gbits + counter.set(self.stats.counters.status, 2) -- down if not conf.vmdq and conf.macaddr then - self.stats.counters.macaddr = counter.open(self.stats.path..'macaddr') counter.set(self.stats.counters.macaddr, macaddress:new(conf.macaddr):int()) end @@ -143,19 +149,37 @@ function Intel82599:add_receive_buffers () end end --- Synchronize self.stats.register a and self.stats.counters. +-- Synchronize self.stats.s/r a and self.stats.counters. +local link_up_mask = lib.bits{Link_up=30} +local promisc_mask = lib.bits{UPE=9} function Intel82599:sync_stats () - local counters, register = self.stats.counters, self.stats.register - counter.set(counters.rxbytes, register.GORC64()) - counter.set(counters.rxpackets, register.GPRC()) - counter.set(counters.rxmcast, register.MPRC()) - counter.set(counters.rxbcast, register.BPRC()) - counter.set(counters.rxdrop, register.TPR() - register.GPRC()) - counter.set(counters.txbytes, register.GOTC64()) - counter.set(counters.txpackets, register.GPTC()) - counter.set(counters.txmcast, register.MPTC()) - counter.set(counters.txbcast, register.BPTC()) - counter.set(counters.txdrop, register.TPT() - register.GPTC()) + local counters = self.stats.counters + local s, r, qs = self.stats.s, self.stats.r, self.stats.qs + counter.set(counters.rxbytes, s.GORC64()) + counter.set(counters.rxpackets, s.GPRC()) + local mprc, bprc = s.MPRC(), s.BPRC() + counter.set(counters.rxmcast, mprc + bprc) + counter.set(counters.rxbcast, bprc) + -- The RX receive drop counts are only available through the RX stats + -- register. We only read stats register #0 here. + counter.set(counters.rxdrop, qs.QPRDC[0]()) + counter.set(counters.rxerrors, s.TPR() - s.GPRC()) + counter.set(counters.txbytes, s.GOTC64()) + counter.set(counters.txpackets, s.GPTC()) + local mptc, bptc = s.MPTC(), s.BPTC() + counter.set(counters.txmcast, mptc + bptc) + counter.set(counters.txbcast, bptc) + counter.set(counters.txerrors, s.TPT() - s.GPTC()) + if bit.band(r.LINKS(), link_up_mask) == link_up_mask then + counter.set(counters.status, 1) -- Up + else + counter.set(counters.status, 2) -- Down + end + if bit.band(r.FCTRL(), promisc_mask) ~= 0ULL then + counter.set(counters.promisc, 1) -- True + else + counter.set(counters.promisc, 2) -- False + end end -- Push packets from our 'rx' link onto the network. @@ -163,6 +187,13 @@ function Intel82599:push () local l = self.input.rx if l == nil then return end while not empty(l) and self.dev:can_transmit() do + -- We must not send packets that are bigger than the MTU. This + -- check is currently disabled to satisfy some selftests until + -- agreement on this strategy is reached. + -- if p.length > self.dev.mtu then + -- counter.add(self.stats.counters.txdrop) + -- packet.free(p) + -- else do local p = receive(l) self.dev:transmit(p) --packet.deref(p) From e9f5dda5452ed2bcb1f1394cabf8b1f604357e15 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 2 Jun 2016 17:37:34 +0200 Subject: [PATCH 24/39] lib.macaddress: add bytes method. --- src/lib/macaddress.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/macaddress.lua b/src/lib/macaddress.lua index e55bdcfb3c..0508df17d6 100644 --- a/src/lib/macaddress.lua +++ b/src/lib/macaddress.lua @@ -46,6 +46,10 @@ function mac_mt:int () return self.bits end +function mac_mt:bytes () + return self.bytes +end + function mac_mt:subbits (i,j) local b = bit.rshift(self.bits, i) local mask = bit.bnot(bit.lshift(0xffffffffffffLL, j-i)) From 6b12e9c3e3e45ce2f60bfac0cf399ba555a03f0f Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 2 Jun 2016 18:45:40 +0200 Subject: [PATCH 25/39] spin out SNMP code from apps.intel.intel10 to lib.ipc.shmem.iftable_mib. --- src/apps/intel/intel10g.lua | 188 +----------------------------- src/lib/ipc/shmem/iftable_mib.lua | 154 ++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 182 deletions(-) create mode 100644 src/lib/ipc/shmem/iftable_mib.lua diff --git a/src/apps/intel/intel10g.lua b/src/apps/intel/intel10g.lua index 51e2d640fa..b5ec3e3ef3 100644 --- a/src/apps/intel/intel10g.lua +++ b/src/apps/intel/intel10g.lua @@ -6,7 +6,8 @@ --- affordable (~$400) network cards made by Intel and others. --- --- You will need to familiarize yourself with the excellent [data ---- sheet]() to understand this module. +--- sheet](http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82599-10-gbe-controller-datasheet.pdf) +--- to understand this module. module(...,package.seeall) @@ -17,7 +18,6 @@ local pci = require("lib.hardware.pci") local register = require("lib.hardware.register") local index_set = require("lib.index_set") local macaddress = require("lib.macaddress") -local mib = require("lib.ipc.shmem.mib") local timer = require("core.timer") local bits, bitset = lib.bits, lib.bitset @@ -49,10 +49,6 @@ local default = { -- The default MTU allows for an IP packet of a total size of 9000 -- bytes without VLAN tagging. mtu = 9014, - - snmp = { - status_timer = 5, -- Interval for IF status check and MIB update - } } local function pass (...) return ... end @@ -79,7 +75,6 @@ function new_sf (conf) rdh = 0, -- Cache of receive head (RDH) register rdt = 0, -- Cache of receive tail (RDT) register rxnext = 0, -- Index of next buffer to receive - snmp = conf.snmp, } return setmetatable(dev, M_sf) end @@ -118,9 +113,6 @@ end --- See data sheet section 4.6.3 "Initialization Sequence." function M_sf:init () - if self.snmp then - self:init_snmp() - end self:init_dma_memory() self.redos = 0 @@ -186,156 +178,6 @@ do end end -function M_sf:init_snmp () - -- Rudimentary population of a row in the ifTable MIB. Allocation - -- of the ifIndex is delegated to the SNMP agent via the name of - -- the interface in ifDescr (currently the PCI address). - local ifTable = mib:new({ directory = self.snmp.directory or nil, - filename = self.pciaddress }) - self.snmp.ifTable = ifTable - -- ifTable - ifTable:register('ifDescr', 'OctetStr', self.pciaddress) - ifTable:register('ifType', 'Integer32', 6) -- ethernetCsmacd - ifTable:register('ifMtu', 'Integer32', self.mtu) - ifTable:register('ifSpeed', 'Gauge32', 10000000) - - -- After a reset of the NIC, the "native" MAC address is copied to - -- the receive address register #0 from the EEPROM - local ral, rah = self.r.RAL[0](), self.r.RAH[0]() - assert(bit.band(rah, bits({ AV = 31 })) == bits({ AV = 31 }), - "MAC address on "..self.pciaddress.." is not valid ") - local mac = ffi.new("struct { uint32_t lo; uint16_t hi; }") - mac.lo = ral - mac.hi = bit.band(rah, 0xFFFF) - ifTable:register('ifPhysAddress', { type = 'OctetStr', length = 6 }, - ffi.string(mac, 6)) - ifTable:register('ifAdminStatus', 'Integer32', 1) -- up - ifTable:register('ifOperStatus', 'Integer32', 2) -- down - ifTable:register('ifLastChange', 'TimeTicks', 0) - ifTable:register('_X_ifLastChange_TicksBase', 'Counter64', C.get_unix_time()) - ifTable:register('ifInOctets', 'Counter32', 0) - ifTable:register('ifInUcastPkts', 'Counter32', 0) - ifTable:register('ifInDiscards', 'Counter32', 0) - ifTable:register('ifInErrors', 'Counter32', 0) -- TBD - ifTable:register('ifInUnknownProtos', 'Counter32', 0) -- TBD - ifTable:register('ifOutOctets', 'Counter32', 0) - ifTable:register('ifOutUcastPkts', 'Counter32', 0) - ifTable:register('ifOutDiscards', 'Counter32', 0) - ifTable:register('ifOutErrors', 'Counter32', 0) -- TBD - -- ifXTable - ifTable:register('ifName', { type = 'OctetStr', length = 255 }, - self.pciaddress) - ifTable:register('ifInMulticastPkts', 'Counter32', 0) - ifTable:register('ifInBroadcastPkts', 'Counter32', 0) - ifTable:register('ifOutMulticastPkts', 'Counter32', 0) - ifTable:register('ifOutBroadcastPkts', 'Counter32', 0) - ifTable:register('ifHCInOctets', 'Counter64', 0) - ifTable:register('ifHCInUcastPkts', 'Counter64', 0) - ifTable:register('ifHCInMulticastPkts', 'Counter64', 0) - ifTable:register('ifHCInBroadcastPkts', 'Counter64', 0) - ifTable:register('ifHCOutOctets', 'Counter64', 0) - ifTable:register('ifHCOutUcastPkts', 'Counter64', 0) - ifTable:register('ifHCOutMulticastPkts', 'Counter64', 0) - ifTable:register('ifHCOutBroadcastPkts', 'Counter64', 0) - ifTable:register('ifLinkUpDownTrapEnable', 'Integer32', 2) -- disabled - ifTable:register('ifHighSpeed', 'Gauge32', 10000) - ifTable:register('ifPromiscuousMode', 'Integer32', 2) -- false - ifTable:register('ifConnectorPresent', 'Integer32', 1) -- true - ifTable:register('ifAlias', { type = 'OctetStr', length = 64 }, - self.pciaddress) -- TBD add description - ifTable:register('ifCounterDiscontinuityTime', 'TimeTicks', 0) -- TBD - ifTable:register('_X_ifCounterDiscontinuityTime', 'Counter64', 0) -- TBD - - --- Create a timer to periodically check the interface status. - --- Static variables are used in the timer function to avoid - --- garbage. - local status = { [1] = 'up', [2] = 'down' } - local mask = bits{Link_up=30} - local promisc = bits({UPE = 9}) - -- Pre-allocate storage for the results of register-reads - local r = { - in_mcast_pkts = { r = self.s.MPRC }, - in_bcast_pkts = { r = self.s.BPRC }, - in_pkts = { r = self.s.GPRC }, - in_octets64 = { r = self.s.GORC64 }, - - out_mcast_pkts = { r = self.s.MPTC }, - out_bcast_pkts = { r = self.s.BPTC }, - out_pkts = { r = self.s.GPTC }, - out_octets64 = { r = self.s.GOTC64 }, - } - local r_keys = {} - for k, _ in pairs(r) do - table.insert(r_keys, k) - r[k].v = ffi.new("uint64_t [1]") - end - local function read_registers() - for _, k in ipairs(r_keys) do - r[k].v[0] = r[k].r() - end - end - self.logger = lib.logger_new({ module = 'intel10g' }) - local t = timer.new("Interface "..self.pciaddress.." status checker", - function(t) - local old = ifTable:get('ifOperStatus') - local new = 1 - if band(self.r.LINKS(), mask) ~= mask then - new = 2 - end - if old ~= new then - self.logger:log("Interface "..self.pciaddress.. - " status change: "..status[old].. - " => "..status[new]) - ifTable:set('ifOperStatus', new) - ifTable:set('ifLastChange', 0) - ifTable:set('_X_ifLastChange_TicksBase', - C.get_unix_time()) - end - - ifTable:set('ifPromiscuousMode', - (bit.band(self.r.FCTRL(), promisc) ~= 0ULL - and 1) or 2) - -- Update counters - read_registers() - ifTable:set('ifHCInMulticastPkts', r.in_mcast_pkts.v[0]) - ifTable:set('ifInMulticastPkts', r.in_mcast_pkts.v[0]) - ifTable:set('ifHCInBroadcastPkts', r.in_bcast_pkts.v[0]) - ifTable:set('ifInBroadcastPkts', r.in_bcast_pkts.v[0]) - local in_ucast_pkts = r.in_pkts.v[0] - r.in_bcast_pkts.v[0] - - r.in_mcast_pkts.v[0] - ifTable:set('ifHCInUcastPkts', in_ucast_pkts) - ifTable:set('ifInUcastPkts', in_ucast_pkts) - ifTable:set('ifHCInOctets', r.in_octets64.v[0]) - ifTable:set('ifInOctets', r.in_octets64.v[0]) - - ifTable:set('ifHCOutMulticastPkts', r.out_mcast_pkts.v[0]) - ifTable:set('ifOutMulticastPkts', r.out_mcast_pkts.v[0]) - ifTable:set('ifHCOutBroadcastPkts', r.out_bcast_pkts.v[0]) - ifTable:set('ifOutBroadcastPkts', r.out_bcast_pkts.v[0]) - local out_ucast_pkts = r.out_pkts.v[0] - r.out_bcast_pkts.v[0] - - r.out_mcast_pkts.v[0] - ifTable:set('ifHCOutUcastPkts', out_ucast_pkts) - ifTable:set('ifOutUcastPkts', out_ucast_pkts) - ifTable:set('ifHCOutOctets', r.out_octets64.v[0]) - ifTable:set('ifOutOctets', r.out_octets64.v[0]) - - -- The RX receive drop counts are only - -- available through the RX stats register. - -- We only read stats register #0 here. See comment - -- in init_statistics() - ifTable:set('ifInDiscards', self.qs.QPRDC[0]()) - - ifTable:set('ifInErrors', self.s.CRCERRS() + - self.s.ILLERRC() + self.s.ERRBC() + - self.s.RUC() + self.s.RFC() + - self.s.ROC() + self.s.RJC()) - end, - 1e9 * (self.snmp.status_timer or - default.snmp.status_timer), 'repeating') - timer.activate(t) - return self -end - function M_sf:global_reset () local reset = bits{LinkReset=3, DeviceReset=26} self.r.CTRL(reset) @@ -448,22 +290,10 @@ end local txdesc_flags = bits{ifcs=25, dext=29, dtyp0=20, dtyp1=21, eop=24} function M_sf:transmit (p) - -- We must not send packets that are bigger than the MTU. This - -- check is currently disabled to satisfy some selftests until - -- agreement on this strategy is reached. - -- if p.length > self.mtu then - -- if self.snmp then - -- local errors = self.snmp.ifTable:ptr('ifOutDiscards') - -- errors[0] = errors[0] + 1 - -- end - -- packet.free(p) - -- else - do - self.txdesc[self.tdt].address = memory.virtual_to_physical(p.data) - self.txdesc[self.tdt].options = bor(p.length, txdesc_flags, lshift(p.length+0ULL, 46)) - self.txpackets[self.tdt] = p - self.tdt = band(self.tdt + 1, num_descriptors - 1) - end + self.txdesc[self.tdt].address = memory.virtual_to_physical(p.data) + self.txdesc[self.tdt].options = bor(p.length, txdesc_flags, lshift(p.length+0ULL, 46)) + self.txpackets[self.tdt] = p + self.tdt = band(self.tdt + 1, num_descriptors - 1) end function M_sf:sync_transmit () @@ -649,7 +479,6 @@ function new_pf (conf) mac_set = index_set:new(127, "MAC address table"), vlan_set = index_set:new(64, "VLAN Filter table"), mirror_set = index_set:new(4, "Mirror pool table"), - snmp = conf.snmp, } return setmetatable(dev, M_pf) end @@ -675,9 +504,6 @@ function M_pf:close() end function M_pf:init () - if self.snmp then - self:init_snmp() - end self.redos = 0 local mask = bits{Link_up=30} for i = 1, 100 do @@ -703,7 +529,6 @@ function M_pf:init () return self end -M_pf.init_snmp = M_sf.init_snmp M_pf.global_reset = M_sf.global_reset M_pf.disable_interrupts = M_sf.disable_interrupts M_pf.set_receive_descriptors = pass @@ -784,7 +609,6 @@ function M_pf:new_vf (poolnum) base = self.base, -- mmap()ed register file s = self.s, -- Statistics registers mtu = self.mtu, - snmp = self.snmp, -- and others are our own r = {}, -- Configuration registers poolnum = poolnum, diff --git a/src/lib/ipc/shmem/iftable_mib.lua b/src/lib/ipc/shmem/iftable_mib.lua new file mode 100644 index 0000000000..0f09aef0aa --- /dev/null +++ b/src/lib/ipc/shmem/iftable_mib.lua @@ -0,0 +1,154 @@ +-- Use of this source code is governed by the Apache 2.0 license; see COPYING. + +module(...,package.seeall) +local mib = require("lib.ipc.shmem.mib") +local counter = require("core.counter") +local macaddress = require("lib.macaddress") +local ffi = require("ffi") + +local iftypes = { + [0x1000] = 6, -- ethernetCsmacd + [0x1001] = 53, -- propVirtual +} + +function init_snmp (name, counters, directory, interval) + -- Rudimentary population of a row in the ifTable MIB. Allocation + -- of the ifIndex is delegated to the SNMP agent via the name of + -- the interface in ifDescr. + local ifTable = mib:new({ directory = directory or nil, + filename = name }) + -- ifTable + ifTable:register('ifDescr', 'OctetStr', name) + ifTable:register('ifType', 'Integer32') + if counters.type then + ifTable:set('ifType', iftypes[counter.read(counters.type)] or 1) -- other + end + ifTable:register('ifMtu', 'Integer32') + if counters.mtu then + ifTable:set('ifMtu', counter.read(counters.mtu)) + end + ifTable:register('ifSpeed', 'Gauge32') + ifTable:register('ifHighSpeed', 'Gauge32') + if counters.speed then + ifTable:set('ifSpeed', counter.read(counters.speed)) + ifTable:set('ifHighSpeed', counter.read(counters.speed) / 1000) + end + ifTable:register('ifPhysAddress', { type = 'OctetStr', length = 6 }) + if counters.macaddr then + local mac = macaddress:new(counter.read(counters.macaddr)) + ifTable:set('ifPhysAddress', ffi.string(mac:bytes(), 6)) + end + ifTable:register('ifAdminStatus', 'Integer32', 1) -- up + ifTable:register('ifOperStatus', 'Integer32', 2) -- down + ifTable:register('ifLastChange', 'TimeTicks', 0) + ifTable:register('_X_ifLastChange_TicksBase', 'Counter64', + C.get_unix_time()) + ifTable:register('ifInOctets', 'Counter32', 0) + ifTable:register('ifInUcastPkts', 'Counter32', 0) + ifTable:register('ifInDiscards', 'Counter32', 0) + ifTable:register('ifInErrors', 'Counter32', 0) -- TBD + ifTable:register('ifInUnknownProtos', 'Counter32', 0) -- TBD + ifTable:register('ifOutOctets', 'Counter32', 0) + ifTable:register('ifOutUcastPkts', 'Counter32', 0) + ifTable:register('ifOutDiscards', 'Counter32', 0) + ifTable:register('ifOutErrors', 'Counter32', 0) -- TBD + -- ifXTable + ifTable:register('ifName', { type = 'OctetStr', length = 255 }, name) + ifTable:register('ifInMulticastPkts', 'Counter32', 0) + ifTable:register('ifInBroadcastPkts', 'Counter32', 0) + ifTable:register('ifOutMulticastPkts', 'Counter32', 0) + ifTable:register('ifOutBroadcastPkts', 'Counter32', 0) + ifTable:register('ifHCInOctets', 'Counter64', 0) + ifTable:register('ifHCInUcastPkts', 'Counter64', 0) + ifTable:register('ifHCInMulticastPkts', 'Counter64', 0) + ifTable:register('ifHCInBroadcastPkts', 'Counter64', 0) + ifTable:register('ifHCOutOctets', 'Counter64', 0) + ifTable:register('ifHCOutUcastPkts', 'Counter64', 0) + ifTable:register('ifHCOutMulticastPkts', 'Counter64', 0) + ifTable:register('ifHCOutBroadcastPkts', 'Counter64', 0) + ifTable:register('ifLinkUpDownTrapEnable', 'Integer32', 2) -- disabled + ifTable:register('ifPromiscuousMode', 'Integer32', 2) -- false + ifTable:register('ifConnectorPresent', 'Integer32', 1) -- true + ifTable:register('ifAlias', { type = 'OctetStr', length = 64 }, + name) -- TBD add description + ifTable:register('ifCounterDiscontinuityTime', 'TimeTicks', 0) + ifTable:register('_X_ifCounterDiscontinuityTime', 'Counter64') + if counters.dtime then + ifTable:set('_X_ifCounterDiscontinuityTime', counter.read(counters.dtime)) + end + + local logger = lib.logger_new({ module = 'iftable_mib' }) + local function t () + local old, new + if counters.status then + old = ifTable:get('ifOperStatus') + new = counter.read(counters.status) + else + new = 1 + end + if old ~= new then + logger:log("Interface "..name.. + " status change: "..status[old].." => "..status[new]) + ifTable:set('ifOperStatus', new) + ifTable:set('ifLastChange', 0) + ifTable:set('_X_ifLastChange_TicksBase', C.get_unix_time()) + end + + if counters.promisc then + ifTable:set('ifPromiscuousMode', counter.read(counters.promisc)) + end + -- Update counters + if counters.rxpackets and counters.rxmcast and counters.rxbcast then + local rxbcast = counter.read(counters.rxbcast) + local rxmcast = counter.read(counters.rxmcast) + local rxpackets = counter.read(counters.rxpackets) + local inMcast = rxmcast - rxbcast + local inUcast = rxpackets - rxmcast + ifTable:set('ifHCInMulticastPkts', inMcast) + ifTable:set('ifInMulticastPkts', inMcast) + ifTable:set('ifHCInBroadcastPkts', rxbcast) + ifTable:set('ifInBroadcastPkts', rxbcast) + ifTable:set('ifHCInUcastPkts', inUcast) + ifTable:set('ifInUcastPkts', inUcast) + end + if counters.rxbytes then + local rxbytes = counter.read(counters.rxbytes) + ifTable:set('ifHCInOctets', rxbytes) + ifTable:set('ifInOctets', rxbytes) + end + if counters.rxdrop then + ifTable:set('ifInDiscards', counter.read(counters.rxdrop)) + end + if counters.rxerrors then + ifTable:set('ifInErrors', counter.read(counters.rxerrors)) + end + if counters.txpackets and counters.txmcast and counters.txbcast then + local txbcast = counter.read(counters.txbcast) + local txmcast = counter.read(counters.txmcast) + local txpackets = counter.read(counters.txpackets) + local outMcast = txmcast - txbcast + local outUcast = txpackets - txmcast + ifTable:set('ifHCOutMulticastPkts', outMcast) + ifTable:set('ifOutMulticastPkts', outMcast) + ifTable:set('ifHCOutBroadcastPkts', txbcast) + ifTable:set('ifOutBroadcastPkts', txbcast) + ifTable:set('ifHCOutUcastPkts', outUcast) + ifTable:set('ifOutUcastPkts', outUcast) + end + if counters.txbytes then + local txbytes = counter.read(counters.txbytes) + ifTable:set('ifHCOutOctets', txbytes) + ifTable:set('ifOutOctets', txbytes) + end + if counters.txdrop then + ifTable:set('ifOutDiscards', counter.read(counters.txdrop)) + end + if counters.txerrors then + ifTable:set('ifOutErrors', counter.read(counters.txerrors)) + end + end + local t = timer.new("Interface "..name.." status checker", + t, 1e9 * (interval or 5), 'repeating') + timer.activate(t) + return t +end From 1c9f58585f7480df16ce3ffe532a1d99286ff1ea Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Tue, 7 Jun 2016 15:47:49 +0200 Subject: [PATCH 26/39] Move model.txt to src/doc/statistics.md. --- model.txt | 48 ----------------------------------- src/doc/statistics.md | 58 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 48 deletions(-) delete mode 100644 model.txt create mode 100644 src/doc/statistics.md diff --git a/model.txt b/model.txt deleted file mode 100644 index 8f4e128823..0000000000 --- a/model.txt +++ /dev/null @@ -1,48 +0,0 @@ -RFC7223 Snabb ifTable MIB -======= ===== =========== -name -description? -type type (~= identity) -enabled? -link-up-down-trap-enable? -admin-status -oper-status status (= enum) ifOperStatus -last-change? -if-index -phys-address? macaddr (uint64) ifPhysAddress -higher-layer-if* -lower-layer-if* -speed? speed ifSpeed -discontinuity-time dtime (seconds since epoch) ifCounterDiscontinuityTime -in-octets? rxbytes ifInOctets - rxpackets -in-unicast-pkts? -in-broadcast-pkts? rxbcast ifInBroadcastPkts - rxmcast -in-multicast-pkts? ifInMulticastPkts -in-discards? rxdrop ifInDiscards -in-errors? rxerrors ifInErrors -in-unknown-protos? -out-octets? txbytes ifOutOctets - txpackets -out-unicast-pkts? -out-broadcast-pkts? txbcast ifOutBroadcastPkts - txmcast -out-multicast-pkts? ifOutMulticastPkts -out-discards? txdrop ifOutDiscards -out-errors? txerrors ifOutErrors - rxpackets - txpackets - rxmcast - txmcast - mtu ifMtu - promisc ifPromiscuousMode - -snabb/type: - -0x1000 Hardware interface -0x1001 Virtual interface - -snabb/bcast, mcast, packets: - -(tx/rx) - bcast ⊆ mcast ⊆ packets diff --git a/src/doc/statistics.md b/src/doc/statistics.md new file mode 100644 index 0000000000..8ba1566a60 --- /dev/null +++ b/src/doc/statistics.md @@ -0,0 +1,58 @@ +### Statistics counters in Snabb + +Below is list of statistics counters defined by Snabb, and their relation to +RFC 7223 and ifTable MIB. All counters are unsigned 64bit integers. Each Snabb +app can optionally implement any number of these counters. + +| Snabb | RFC 7223 | ifTable MIB +| ----- | -------- | ----------- +| | name | +| | description? | ifDescr +| type (~= identity) | type | ifType +| | enabled? | +| | link-up-down-trap-enable? | +| | admin-status | +| status (= enum) | oper-status | ifOperStatus +| | last-change? | ifLastChange +| | if-index | +| macaddr (uint64) | phys-address? | ifPhysAddress +| | higher-layer-if* | +| | lower-layer-if* | +| speed | speed? | ifSpeed +| dtime (seconds since epoch) | discontinuity-time | ifCounterDiscontinuityTime +| rxbytes | in-octets? | ifInOctets +| rxpackets | | +| | in-unicast-pkts? | +| rxbcast | in-broadcast-pkts? | ifInBroadcastPkts +| rxmcast | | +| | in-multicast-pkts? | ifInMulticastPkts +| rxdrop | in-discards? | ifInDiscards +| rxerrors | in-errors? | ifInErrors +| | in-unknown-protos? | +| txbytes | out-octets? | ifOutOctets +| txpackets | | +| | out-unicast-pkts? | +| txbcast | out-broadcast-pkts? | ifOutBroadcastPkts +| txmcast | | +| | out-multicast-pkts? | ifOutMulticastPkts +| txdrop | out-discards? | ifOutDiscards +| txerrors | out-errors? | ifOutErrors +| rxpackets | | +| txpackets | | +| rxmcast | | +| txmcast | | +| mtu | | ifMtu +| promisc | | ifPromiscuousMode + + +#### type + + - `0x1000` Hardware interface + - `0x1001` Virtual interface + +#### rxbcast, rxmcast, rxpackets, txbcast, txmcast, txpackets + +Snabb defines total packet counts, multicast packet counts (packets with group +bit set) and broadcast packet counts for both input and output. + + E.g.: (tx/rx)bcast ⊆ (tx/rx)mcast ⊆ (tx/rx)packets From 339f8f1951256bb63273e47089aa8230b432346d Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Tue, 7 Jun 2016 16:27:24 +0200 Subject: [PATCH 27/39] apps.intel_app: Revert rxerrors calculation, remove broken txerrors. --- src/apps/intel/intel_app.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 623fb1a3a3..0e463d1e32 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -163,13 +163,13 @@ function Intel82599:sync_stats () -- The RX receive drop counts are only available through the RX stats -- register. We only read stats register #0 here. counter.set(counters.rxdrop, qs.QPRDC[0]()) - counter.set(counters.rxerrors, s.TPR() - s.GPRC()) + counter.set(counters.rxerrors, s.CRCERRS() + s.ILLERRC() + s.ERRBC() + + s.RUC() + s.RFC() + s.ROC() + s.RJC()) counter.set(counters.txbytes, s.GOTC64()) counter.set(counters.txpackets, s.GPTC()) local mptc, bptc = s.MPTC(), s.BPTC() counter.set(counters.txmcast, mptc + bptc) counter.set(counters.txbcast, bptc) - counter.set(counters.txerrors, s.TPT() - s.GPTC()) if bit.band(r.LINKS(), link_up_mask) == link_up_mask then counter.set(counters.status, 1) -- Up else From 7ec7f807d15484f531ca31b56d671e0939b06fb4 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Tue, 7 Jun 2016 16:30:57 +0200 Subject: [PATCH 28/39] Revert "lib.macaddress: add bytes method." and remove int method. This reverts commit e9f5dda5452ed2bcb1f1394cabf8b1f604357e15. --- src/apps/intel/intel_app.lua | 4 ++-- src/lib/ipc/shmem/iftable_mib.lua | 2 +- src/lib/macaddress.lua | 8 -------- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 0e463d1e32..fc5829c723 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -77,7 +77,7 @@ function Intel82599:new (arg) counter.set(self.stats.counters.status, 2) -- down if not conf.vmdq and conf.macaddr then counter.set(self.stats.counters.macaddr, - macaddress:new(conf.macaddr):int()) + macaddress:new(conf.macaddr).bits) end end return setmetatable(self, Intel82599) @@ -118,7 +118,7 @@ function Intel82599:reconfig(arg) if not self.dev.pf and conf.macaddr then counter.set(self.stats.counters.macaddr, - macaddress:new(conf.macaddr):int()) + macaddress:new(conf.macaddr).bits) end end diff --git a/src/lib/ipc/shmem/iftable_mib.lua b/src/lib/ipc/shmem/iftable_mib.lua index 0f09aef0aa..8d96d29e67 100644 --- a/src/lib/ipc/shmem/iftable_mib.lua +++ b/src/lib/ipc/shmem/iftable_mib.lua @@ -36,7 +36,7 @@ function init_snmp (name, counters, directory, interval) ifTable:register('ifPhysAddress', { type = 'OctetStr', length = 6 }) if counters.macaddr then local mac = macaddress:new(counter.read(counters.macaddr)) - ifTable:set('ifPhysAddress', ffi.string(mac:bytes(), 6)) + ifTable:set('ifPhysAddress', ffi.string(mac.bytes, 6)) end ifTable:register('ifAdminStatus', 'Integer32', 1) -- up ifTable:register('ifOperStatus', 'Integer32', 2) -- down diff --git a/src/lib/macaddress.lua b/src/lib/macaddress.lua index 0508df17d6..a973b1d7b3 100644 --- a/src/lib/macaddress.lua +++ b/src/lib/macaddress.lua @@ -42,14 +42,6 @@ function mac_mt.__eq (a, b) return a.bits == b.bits end -function mac_mt:int () - return self.bits -end - -function mac_mt:bytes () - return self.bytes -end - function mac_mt:subbits (i,j) local b = bit.rshift(self.bits, i) local mask = bit.bnot(bit.lshift(0xffffffffffffLL, j-i)) From 327804f56d2512cce7967b26fe8bbab726178aaf Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Thu, 9 Jun 2016 17:09:14 +0200 Subject: [PATCH 29/39] doc/statistics.md: remove duplicates. --- src/doc/statistics.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/doc/statistics.md b/src/doc/statistics.md index 8ba1566a60..852d77829f 100644 --- a/src/doc/statistics.md +++ b/src/doc/statistics.md @@ -37,10 +37,6 @@ app can optionally implement any number of these counters. | | out-multicast-pkts? | ifOutMulticastPkts | txdrop | out-discards? | ifOutDiscards | txerrors | out-errors? | ifOutErrors -| rxpackets | | -| txpackets | | -| rxmcast | | -| txmcast | | | mtu | | ifMtu | promisc | | ifPromiscuousMode From 90fc893bef4caa2260793b672ad8ef29f1d7591c Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Fri, 10 Jun 2016 13:00:02 +0200 Subject: [PATCH 30/39] core.app: fix bug in #766 introduced while merging 6cac870a. --- src/core/app.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/app.lua b/src/core/app.lua index dbcc7f7fa9..94d08a0e23 100644 --- a/src/core/app.lua +++ b/src/core/app.lua @@ -72,20 +72,21 @@ end function with_restart (app, method) local oldshm = shm.path shm.path = app.shmpath + local status, result if use_restart then -- Run fn in protected mode using pcall. - local status, result_or_error = pcall(method, app) + status, result = pcall(method, app) -- If pcall caught an error mark app as "dead" (record time and cause -- of death). if not status then - app.dead = { error = result_or_error, time = now() } + app.dead = { error = result, time = now() } end - return status, result_or_error else - return true, method(app) + status, result = true, method(app) end shm.path = oldshm + return status, result_or_error end -- Restart dead apps. From ceb080f6dd40ea5d7173197d2e06e3fc6e16dcf1 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Fri, 10 Jun 2016 13:10:20 +0200 Subject: [PATCH 31/39] core.app: fix bug in #766, use app.shmpath during reconfig. --- src/core/app.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/app.lua b/src/core/app.lua index 94d08a0e23..38a1973bf3 100644 --- a/src/core/app.lua +++ b/src/core/app.lua @@ -206,7 +206,10 @@ function apply_config_actions (actions, conf) if app_table[name].reconfig then local arg = conf.apps[name].arg local app = app_table[name] + local shmorig = shm.path + shm.path = app.shmpath app:reconfig(arg) + shm.path = shmorig new_app_table[name] = app table.insert(new_app_array, app) app_name_to_index[name] = #new_app_array From d82729abf465c0e9a800e7de276d00f144c04b3a Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Fri, 10 Jun 2016 14:41:58 +0200 Subject: [PATCH 32/39] core.app: fix typo in previous commit. --- src/core/app.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/app.lua b/src/core/app.lua index 38a1973bf3..5d7fa0e8fd 100644 --- a/src/core/app.lua +++ b/src/core/app.lua @@ -86,7 +86,7 @@ function with_restart (app, method) status, result = true, method(app) end shm.path = oldshm - return status, result_or_error + return status, result end -- Restart dead apps. From 5488c68cb5afb243e5006f000c2f33556cf44031 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 8 Jun 2016 17:44:10 +0200 Subject: [PATCH 33/39] lib.protocol.ethernet: add is_bcast and n_bcast. --- src/lib/protocol/README.md | 9 +++++++++ src/lib/protocol/ethernet.lua | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/lib/protocol/README.md b/src/lib/protocol/README.md index 4a13483a4b..a20093cf60 100644 --- a/src/lib/protocol/README.md +++ b/src/lib/protocol/README.md @@ -107,6 +107,15 @@ Returns a true value if *mac* address denotes a [Multicast address](https://en.w Returns 1 if *mac* address denotes a [Multicast address](https://en.wikipedia.org/wiki/Multicast_address#Ethernet) and 0 otherwise. +— Function **ethernet:is_bcast** *mac* + +Returns a true value if *mac* address denotes a [Broadcast address](https://en.wikipedia.org/wiki/Broadcast_address#Ethernet). + +— Function **ethernet:n_bcast** *mac* + +Returns 1 if *mac* address denotes a [Broadcast address](https://en.wikipedia.org/wiki/Broadcast_address#Ethernet) +and 0 otherwise. + — Function **ethernet:ipv6_mcast** *ip* Returns the MAC address for IPv6 multicast *ip* as defined by RFC2464, diff --git a/src/lib/protocol/ethernet.lua b/src/lib/protocol/ethernet.lua index b5abed8d31..2fcf3cf71d 100644 --- a/src/lib/protocol/ethernet.lua +++ b/src/lib/protocol/ethernet.lua @@ -86,6 +86,17 @@ function ethernet:is_mcast (addr) return ethernet:n_mcast(addr) ~= 0 end +-- Return 1 if MAC address is the broadcast address and 0 otherwise +local bcast_address = ethernet:pton("FF:FF:FF:FF:FF:FF") +function ethernet:n_bcast (addr) + return C.memcmp(addr, bcast_address, 6) == 0 +end + +-- Check whether a MAC address is the broadcast address +function ethernet:is_bcast (addr) + return ethernet:n_bcast(addr) ~= 0 +end + -- Instance methods function ethernet:src (a) From 9044c6f0a6f0cae2a2c5c46ff17e5dd1eeb33e94 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 8 Jun 2016 17:44:43 +0200 Subject: [PATCH 34/39] apps.vhost.vhost_user: count rxbcast/txbcast. --- src/apps/vhost/vhost_user.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/apps/vhost/vhost_user.lua b/src/apps/vhost/vhost_user.lua index fe2711769e..85fd3198f0 100644 --- a/src/apps/vhost/vhost_user.lua +++ b/src/apps/vhost/vhost_user.lua @@ -31,8 +31,8 @@ VhostUser = {} local provided_counters = { 'type', 'dtime', - 'rxbytes', 'rxpackets', 'rxmcast', 'rxdrop', - 'txbytes', 'txpackets', 'txmcast' + 'rxbytes', 'rxpackets', 'rxmcast', 'rxbcast', 'rxdrop', + 'txbytes', 'txpackets', 'txmcast', 'txbcast' } function VhostUser:new (args) @@ -108,12 +108,14 @@ function VhostUser:tx_callback (p) counter.add(self.counters.txbytes, packet.length(p)) counter.add(self.counters.txpackets) counter.add(self.counters.txmcast, ethernet:n_mcast(packet.data(p))) + counter.add(self.counters.txbcast, ethernet:n_bcast(packet.data(p))) end function VhostUser:rx_callback (p) counter.add(self.counters.rxbytes, packet.length(p)) counter.add(self.counters.rxpackets) counter.add(self.counters.rxmcast, ethernet:n_mcast(packet.data(p))) + counter.add(self.counters.rxbcast, ethernet:n_bcast(packet.data(p))) end function VhostUser:rxdrop_callback (p) From 5a0161230e305e37f74149c098b5705b20ea315e Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 8 Jun 2016 17:45:07 +0200 Subject: [PATCH 35/39] apps.tap.tap: add statistics counters. --- src/apps/tap/tap.lua | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/apps/tap/tap.lua b/src/apps/tap/tap.lua index 497399d11a..fe803a7b70 100644 --- a/src/apps/tap/tap.lua +++ b/src/apps/tap/tap.lua @@ -5,6 +5,8 @@ module(..., package.seeall) local S = require("syscall") local link = require("core.link") local packet = require("core.packet") +local counter = require("core.counter") +local ethernet = require("lib.protocol.ethernet") local ffi = require("ffi") local C = ffi.C local const = require("syscall.linux.constants") @@ -14,6 +16,12 @@ local t = S.types.t Tap = { } +local provided_counters = { + 'type', 'dtime', + 'rxbytes', 'rxpackets', 'rxmcast', 'rxbcast', + 'txbytes', 'txpackets', 'txmcast', 'txbcast' +} + function Tap:new (name) assert(name, "missing tap interface name") @@ -27,8 +35,14 @@ function Tap:new (name) sock:close() error("Error opening /dev/net/tun: " .. tostring(err)) end - - return setmetatable({sock = sock, name = name}, {__index = Tap}) + local counters = {} + for _, name in ipairs(provided_counters) do + counters[name] = counter.open(name) + end + counter.set(counters.type, 0x1001) -- Virtual interface + counter.set(counters.dtime, C.get_unix_time()) + return setmetatable({sock = sock, name = name, counters = counters}, + {__index = Tap}) end function Tap:pull () @@ -49,6 +63,10 @@ function Tap:pull () end p.length = len link.transmit(l, p) + counter.add(self.counters.rxbytes, len) + counter.add(self.counters.rxpackets) + counter.add(self.counters.rxmcast, ethernet:n_mcast(p.data)) + counter.add(self.counters.rxbcast, ethernet:n_bcast(p.data)) end end @@ -66,6 +84,10 @@ function Tap:push () if len ~= p.length and err.errno == const.E.AGAIN then return end + counter.add(self.counters.txbytes, len) + counter.add(self.counters.txpackets) + counter.add(self.counters.txmcast, ethernet:n_mcast(p.data)) + counter.add(self.counters.txbcast, ethernet:n_bcast(p.data)) -- The write completed so dequeue it from the link and free the packet link.receive(l) packet.free(p) @@ -74,6 +96,8 @@ end function Tap:stop() self.sock:close() + -- delete counters + for name, _ in pairs(self.counters) do counter.delete(name) end end function selftest() From 6233d1532e00ecd6c399b4db57da96146daa6c3b Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Mon, 13 Jun 2016 18:21:55 +0200 Subject: [PATCH 36/39] core.packet: make max_payload public. --- src/README.md | 4 ++++ src/core/packet.lua | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/README.md b/src/README.md index 92ed940bed..1a5eb2a993 100644 --- a/src/README.md +++ b/src/README.md @@ -316,6 +316,10 @@ not been transmitted or freed. The number of allocatable packets is limited by the size of the underlying "freelist", e.g. a pool of unused packet objects from and to which packets are allocated and freed. +— Variable **packet.max_payload** + +Maximum payload length for packets. Read-only. + — Function **packet.allocate** Returns a new empty packet. An an error is raised if there are no packets diff --git a/src/core/packet.lua b/src/core/packet.lua index 6c4ed5311b..143bfdf920 100644 --- a/src/core/packet.lua +++ b/src/core/packet.lua @@ -17,7 +17,7 @@ local packet_t = ffi.typeof("struct packet") local packet_ptr_t = ffi.typeof("struct packet *") local packet_size = ffi.sizeof(packet_t) local header_size = 8 -local max_payload = tonumber(C.PACKET_PAYLOAD_SIZE) +max_payload = tonumber(C.PACKET_PAYLOAD_SIZE) -- Freelist containing empty packets ready for use. From 788438e8898156796289a23afc7a597362cea377 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Wed, 8 Jun 2016 17:45:18 +0200 Subject: [PATCH 37/39] apps.socket.raw: add statistics counters. --- src/apps/socket/raw.lua | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/apps/socket/raw.lua b/src/apps/socket/raw.lua index 24ad578d5d..cd0f9799fa 100644 --- a/src/apps/socket/raw.lua +++ b/src/apps/socket/raw.lua @@ -7,6 +7,8 @@ local h = require("syscall.helpers") local bit = require("bit") local link = require("core.link") local packet = require("core.packet") +local counter = require("core.counter") +local ethernet = require("lib.protocol.ethernet") local ffi = require("ffi") local C = ffi.C @@ -21,6 +23,12 @@ local c, t = S.c, S.types.t RawSocket = {} +local provided_counters = { + 'type', 'dtime', + 'rxbytes', 'rxpackets', 'rxmcast', 'rxbcast', + 'txbytes', 'txpackets', 'txmcast', 'txbcast' +} + function RawSocket:new (ifname) assert(ifname) local index, err = S.util.if_nametoindex(ifname) @@ -40,7 +48,16 @@ function RawSocket:new (ifname) sock:close() error(err) end - return setmetatable({sock = sock}, {__index = RawSocket}) + local counters = {} + for _, name in ipairs(provided_counters) do + counters[name] = counter.open(name) + end + counter.set(counters.type, 0x1001) -- Virtual interface + counter.set(counters.dtime, C.get_unix_time()) + return setmetatable({sock = sock, + rx_p = packet.allocate(), + counters = counters}, + {__index = RawSocket}) end function RawSocket:pull () @@ -61,10 +78,14 @@ function RawSocket:can_receive () end function RawSocket:receive () - local buffer = ffi.new("uint8_t[?]", C.PACKET_PAYLOAD_SIZE) - local sz, err = S.read(self.sock, buffer, C.PACKET_PAYLOAD_SIZE) - assert(sz, err) - return packet.from_pointer(buffer, sz) + local p = self.rx_p + local sz = assert(S.read(self.sock, p.data, packet.max_payload)) + p.length = sz + counter.add(self.counters.rxbytes, sz) + counter.add(self.counters.rxpackets) + counter.add(self.counters.rxmcast, ethernet:n_mcast(p.data)) + counter.add(self.counters.rxbcast, ethernet:n_bcast(p.data)) + return packet.clone(p) end function RawSocket:push () @@ -73,6 +94,10 @@ function RawSocket:push () while not link.empty(l) and self:can_transmit() do local p = link.receive(l) self:transmit(p) + counter.add(self.counters.txbytes, p.length) + counter.add(self.counters.txpackets) + counter.add(self.counters.txmcast, ethernet:n_mcast(p.data)) + counter.add(self.counters.txbcast, ethernet:n_bcast(p.data)) packet.free(p) end end @@ -94,6 +119,9 @@ end function RawSocket:stop() self.sock:close() + packet.free(self.rx_p) + -- delete counters + for name, _ in pairs(self.counters) do counter.delete(name) end end function selftest () From e3fcbbfbe5d3c6a2ec41fb13e8d02cae2b53afc7 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Mon, 4 Jul 2016 14:38:18 +0200 Subject: [PATCH 38/39] Revert "lib.protocol.ethernet: Add n_mcast, branch-free Multicast predicate." Reason: its actually slower than the initial naive version. This reverts commit c186591e312fb90c4c2d847be01255fb7dba0c8b. # Conflicts: # src/apps/vhost/vhost_user.lua --- src/apps/vhost/vhost_user.lua | 8 ++++++-- src/lib/protocol/README.md | 9 --------- src/lib/protocol/ethernet.lua | 7 +------ 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/apps/vhost/vhost_user.lua b/src/apps/vhost/vhost_user.lua index fe2711769e..0bc822bcae 100644 --- a/src/apps/vhost/vhost_user.lua +++ b/src/apps/vhost/vhost_user.lua @@ -107,13 +107,17 @@ end function VhostUser:tx_callback (p) counter.add(self.counters.txbytes, packet.length(p)) counter.add(self.counters.txpackets) - counter.add(self.counters.txmcast, ethernet:n_mcast(packet.data(p))) + if ethernet:is_mcast(packet.data(p)) then + counter.add(self.counters.txmcast) + end end function VhostUser:rx_callback (p) counter.add(self.counters.rxbytes, packet.length(p)) counter.add(self.counters.rxpackets) - counter.add(self.counters.rxmcast, ethernet:n_mcast(packet.data(p))) + if ethernet:is_mcast(packet.data(p)) then + counter.add(self.counters.rxmcast) + end end function VhostUser:rxdrop_callback (p) diff --git a/src/lib/protocol/README.md b/src/lib/protocol/README.md index 4a13483a4b..6f239687d7 100644 --- a/src/lib/protocol/README.md +++ b/src/lib/protocol/README.md @@ -98,15 +98,6 @@ Returns the binary representation of MAC address denoted by *string*. Returns the string representation of *mac* address. -— Function **ethernet:is_mcast** *mac* - -Returns a true value if *mac* address denotes a [Multicast address](https://en.wikipedia.org/wiki/Multicast_address#Ethernet). - -— Function **ethernet:n_mcast** *mac* - -Returns 1 if *mac* address denotes a [Multicast address](https://en.wikipedia.org/wiki/Multicast_address#Ethernet) -and 0 otherwise. - — Function **ethernet:ipv6_mcast** *ip* Returns the MAC address for IPv6 multicast *ip* as defined by RFC2464, diff --git a/src/lib/protocol/ethernet.lua b/src/lib/protocol/ethernet.lua index b5abed8d31..0a55c20f1b 100644 --- a/src/lib/protocol/ethernet.lua +++ b/src/lib/protocol/ethernet.lua @@ -76,14 +76,9 @@ function ethernet:ipv6_mcast(ip) return result end --- Return 1 if MAC address has its group bit set and 0 otherwise -function ethernet:n_mcast (addr) - return band(addr[0], 0x01) -end - -- Check whether a MAC address has its group bit set function ethernet:is_mcast (addr) - return ethernet:n_mcast(addr) ~= 0 + return band(addr[0], 0x01) ~= 0 end -- Instance methods From b418f8f7f6fe1a1ed993186fe98a276b46c729e0 Mon Sep 17 00:00:00 2001 From: Max Rottenkolber Date: Mon, 4 Jul 2016 15:50:29 +0200 Subject: [PATCH 39/39] =?UTF-8?q?intel=5Fapp:=20fix=20wrong=20=E2=80=9Cspe?= =?UTF-8?q?ed=E2=80=9D=20counter=20value.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apps/intel/intel_app.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 92f97d00fd..c85c2e5530 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -73,7 +73,7 @@ function Intel82599:new (arg) counter.set(self.stats.counters.type, 0x1000) -- Hardware interface counter.set(self.stats.counters.dtime, C.get_unix_time()) counter.set(self.stats.counters.mtu, self.dev.mtu) - counter.set(self.stats.counters.speed, 10000000) -- 10 Gbits + counter.set(self.stats.counters.speed, 10000000000) -- 10 Gbits counter.set(self.stats.counters.status, 2) -- down if not conf.vmdq and conf.macaddr then counter.set(self.stats.counters.macaddr,