<commit_before>module(..., package.seeall)

local S          = require("syscall")
local config     = require("core.config")
local csv_stats  = require("program.lwaftr.csv_stats")
local lib        = require("core.lib")
local numa       = require("lib.numa")
local setup      = require("program.lwaftr.setup")
local ingress_drop_monitor = require("lib.timers.ingress_drop_monitor")
local lwutil = require("apps.lwaftr.lwutil")

local fatal, file_exists = lwutil.fatal, lwutil.file_exists
local nic_exists = lwutil.nic_exists

local function show_usage(exit_code)
   print(require("program.lwaftr.run.README_inc"))
   if exit_code then main.exit(exit_code) end
end

function parse_args(args)
   if #args == 0 then show_usage(1) end
   local conf_file, v4, v6
   local ring_buffer_size
   local opts = { verbosity = 0, bench_file = 'bench.csv' }
   local scheduling = { ingress_drop_monitor = 'flush' }
   local handlers = {}
   function handlers.n (arg) opts.name = assert(arg) end
   function handlers.v () opts.verbosity = opts.verbosity + 1 end
   function handlers.i () opts.virtio_net = true end
   function handlers.D (arg)
      opts.duration = assert(tonumber(arg), "duration must be a number")
      assert(opts.duration >= 0, "duration can't be negative")
   end
   function handlers.c(arg)
      conf_file = arg
      if not file_exists(conf_file) then
         fatal(("Couldn't locate configuration file at %s"):format(conf_file))
      end
   end
   function handlers.cpu(arg)
      local cpu = tonumber(arg)
      if not cpu or cpu ~= math.floor(cpu) or cpu < 0 then
         fatal("Invalid cpu number: "..arg)
      end
      scheduling.cpu = cpu
   end
   handlers['real-time'] = function(arg)
      scheduling.real_time = true
   end
   function handlers.v4(arg)
      v4 = arg
      if not nic_exists(v4) then
         fatal(("Couldn't locate NIC with PCI address '%s'"):format(v4))
      end
   end
   handlers["v4-pci"] = function(arg)
      print("WARNING: Deprecated argument '--v4-pci'. Use '--v4' instead.")
      handlers.v4(arg)
   end
   function handlers.v6(arg)
      v6 = arg
      if not nic_exists(v6) then
         fatal(("Couldn't locate NIC with PCI address '%s'"):format(v6))
      end
   end
   handlers["v6-pci"] = function(arg)
      print("WARNING: Deprecated argument '--v6-pci'. Use '--v6' instead.")
      handlers.v6(arg)
   end
   function handlers.r (arg)
      ring_buffer_size = tonumber(arg)
   end
   handlers["on-a-stick"] = function(arg)
      opts["on-a-stick"] = true
      v4 = arg
      if not nic_exists(v4) then
         fatal(("Couldn't locate NIC with PCI address '%s'"):format(v4))
      end
   end
   handlers["mirror"] = function (ifname)
      opts["mirror"] = ifname
   end
   function handlers.y() opts.hydra = true end
   function handlers.b(arg) opts.bench_file = arg end
   handlers["ingress-drop-monitor"] = function (arg)
      if arg == 'flush' or arg == 'warn' then
         scheduling.ingress_drop_monitor = arg
      elseif arg == 'off' then
         scheduling.ingress_drop_monitor = false
      else
         fatal("invalid --ingress-drop-monitor argument: " .. arg
                  .." (valid values: flush, warn, off)")
      end
   end
   function handlers.reconfigurable() opts.reconfigurable = true end
   function handlers.h() show_usage(0) end
   lib.dogetopt(args, handlers, "b:c:vD:yhir:n:",
      { conf = "c", v4 = 1, v6 = 1, ["v4-pci"] = 1, ["v6-pci"] = 1,
        verbose = "v", duration = "D", help = "h", virtio = "i", cpu = 1,
        ["ring-buffer-size"] = "r", ["real-time"] = 0, ["bench-file"] = "b",
        ["ingress-drop-monitor"] = 1, ["on-a-stick"] = 1, mirror = 1,
        hydra = "y", reconfigurable = 0, name="n" })
   if ring_buffer_size ~= nil then
      if opts.virtio_net then
         fatal("setting --ring-buffer-size does not work with --virtio")
      end
      require("apps.intel.intel10g").ring_buffer_size(ring_buffer_size)
   end
   if not conf_file then fatal("Missing required --conf argument.") end
   if opts.mirror then
      assert(opts["on-a-stick"], "Mirror option is only valid in on-a-stick mode")
   end
   if opts["on-a-stick"] then
      scheduling.pci_addrs = { v4 }
      return opts, scheduling, conf_file, v4
   else
      if not v4 then fatal("Missing required --v4 argument.") end
      if not v6 then fatal("Missing required --v6 argument.") end
      scheduling.pci_addrs = { v4, v6 }
      return opts, scheduling, conf_file, v4, v6
   end
end

-- Requires a V4V6 splitter if running in on-a-stick mode and VLAN tag values
-- are the same for the internal and external interfaces.
local function requires_splitter (opts, conf)
   if opts["on-a-stick"] then
      local internal_interface = conf.softwire_config.internal_interface
      local external_interface = conf.softwire_config.external_interface
      return internal_interface.vlan_tag == external_interface.vlan_tag
   end
   return false
end

function run(args)
   local opts, scheduling, conf_file, v4, v6 = parse_args(args)
   local conf = require('apps.lwaftr.conf').load_lwaftr_config(conf_file)
   local use_splitter = requires_splitter(opts, conf)

   -- If there is a name defined on the command line, it should override
   -- anything defined in the config.
   if opts.name then
      conf.softwire_config.name = opts.name
   end

   local c = config.new()
   local setup_fn, setup_args
   if opts.virtio_net then
      setup_fn, setup_args = setup.load_virt, { 'inetNic', v4, 'b4sideNic', v6 }
   elseif opts["on-a-stick"] then
      setup_fn = setup.load_on_a_stick
      setup_args =
         { { v4_nic_name = 'inetNic', v6_nic_name = 'b4sideNic',
             v4v6 = use_splitter and 'v4v6', pciaddr = v4,
             mirror = opts.mirror } }
   else
      setup_fn, setup_args = setup.load_phy, { 'inetNic', v4, 'b4sideNic', v6 }
   end

   if opts.reconfigurable then
      setup.reconfigurable(scheduling, setup_fn, c, conf, unpack(setup_args))
   else
      setup.apply_scheduling(scheduling)
      setup_fn(c, conf, unpack(setup_args))
      setup.validate_config(conf)
   end

   engine.configure(c)

   if opts.verbosity >= 2 then
      local function lnicui_info() engine.report_apps() end
      local t = timer.new("report", lnicui_info, 1e9, 'repeating')
      timer.activate(t)
   end

   if opts.verbosity >= 1 then
      function add_csv_stats_for_pid(pid)
         local csv = csv_stats.CSVStatsTimer:new(opts.bench_file, opts.hydra, pid)
         -- Link names like "tx" are from the app's perspective, but
         -- these labels are from the perspective of the lwAFTR as a<commit_msg>Fix small nit with validate_config call<commit_after>module(..., package.seeall)

local S          = require("syscall")
local config     = require("core.config")
local csv_stats  = require("program.lwaftr.csv_stats")
local lib        = require("core.lib")
local numa       = require("lib.numa")
local setup      = require("program.lwaftr.setup")
local ingress_drop_monitor = require("lib.timers.ingress_drop_monitor")
local lwutil = require("apps.lwaftr.lwutil")

local fatal, file_exists = lwutil.fatal, lwutil.file_exists
local nic_exists = lwutil.nic_exists

local function show_usage(exit_code)
   print(require("program.lwaftr.run.README_inc"))
   if exit_code then main.exit(exit_code) end
end

function parse_args(args)
   if #args == 0 then show_usage(1) end
   local conf_file, v4, v6
   local ring_buffer_size
   local opts = { verbosity = 0, bench_file = 'bench.csv' }
   local scheduling = { ingress_drop_monitor = 'flush' }
   local handlers = {}
   function handlers.n (arg) opts.name = assert(arg) end
   function handlers.v () opts.verbosity = opts.verbosity + 1 end
   function handlers.i () opts.virtio_net = true end
   function handlers.D (arg)
      opts.duration = assert(tonumber(arg), "duration must be a number")
      assert(opts.duration >= 0, "duration can't be negative")
   end
   function handlers.c(arg)
      conf_file = arg
      if not file_exists(conf_file) then
         fatal(("Couldn't locate configuration file at %s"):format(conf_file))
      end
   end
   function handlers.cpu(arg)
      local cpu = tonumber(arg)
      if not cpu or cpu ~= math.floor(cpu) or cpu < 0 then
         fatal("Invalid cpu number: "..arg)
      end
      scheduling.cpu = cpu
   end
   handlers['real-time'] = function(arg)
      scheduling.real_time = true
   end
   function handlers.v4(arg)
      v4 = arg
      if not nic_exists(v4) then
         fatal(("Couldn't locate NIC with PCI address '%s'"):format(v4))
      end
   end
   handlers["v4-pci"] = function(arg)
      print("WARNING: Deprecated argument '--v4-pci'. Use '--v4' instead.")
      handlers.v4(arg)
   end
   function handlers.v6(arg)
      v6 = arg
      if not nic_exists(v6) then
         fatal(("Couldn't locate NIC with PCI address '%s'"):format(v6))
      end
   end
   handlers["v6-pci"] = function(arg)
      print("WARNING: Deprecated argument '--v6-pci'. Use '--v6' instead.")
      handlers.v6(arg)
   end
   function handlers.r (arg)
      ring_buffer_size = tonumber(arg)
   end
   handlers["on-a-stick"] = function(arg)
      opts["on-a-stick"] = true
      v4 = arg
      if not nic_exists(v4) then
         fatal(("Couldn't locate NIC with PCI address '%s'"):format(v4))
      end
   end
   handlers["mirror"] = function (ifname)
      opts["mirror"] = ifname
   end
   function handlers.y() opts.hydra = true end
   function handlers.b(arg) opts.bench_file = arg end
   handlers["ingress-drop-monitor"] = function (arg)
      if arg == 'flush' or arg == 'warn' then
         scheduling.ingress_drop_monitor = arg
      elseif arg == 'off' then
         scheduling.ingress_drop_monitor = false
      else
         fatal("invalid --ingress-drop-monitor argument: " .. arg
                  .." (valid values: flush, warn, off)")
      end
   end
   function handlers.reconfigurable() opts.reconfigurable = true end
   function handlers.h() show_usage(0) end
   lib.dogetopt(args, handlers, "b:c:vD:yhir:n:",
      { conf = "c", v4 = 1, v6 = 1, ["v4-pci"] = 1, ["v6-pci"] = 1,
        verbose = "v", duration = "D", help = "h", virtio = "i", cpu = 1,
        ["ring-buffer-size"] = "r", ["real-time"] = 0, ["bench-file"] = "b",
        ["ingress-drop-monitor"] = 1, ["on-a-stick"] = 1, mirror = 1,
        hydra = "y", reconfigurable = 0, name="n" })
   if ring_buffer_size ~= nil then
      if opts.virtio_net then
         fatal("setting --ring-buffer-size does not work with --virtio")
      end
      require("apps.intel.intel10g").ring_buffer_size(ring_buffer_size)
   end
   if not conf_file then fatal("Missing required --conf argument.") end
   if opts.mirror then
      assert(opts["on-a-stick"], "Mirror option is only valid in on-a-stick mode")
   end
   if opts["on-a-stick"] then
      scheduling.pci_addrs = { v4 }
      return opts, scheduling, conf_file, v4
   else
      if not v4 then fatal("Missing required --v4 argument.") end
      if not v6 then fatal("Missing required --v6 argument.") end
      scheduling.pci_addrs = { v4, v6 }
      return opts, scheduling, conf_file, v4, v6
   end
end

-- Requires a V4V6 splitter if running in on-a-stick mode and VLAN tag values
-- are the same for the internal and external interfaces.
local function requires_splitter (opts, conf)
   if opts["on-a-stick"] then
      local internal_interface = conf.softwire_config.internal_interface
      local external_interface = conf.softwire_config.external_interface
      return internal_interface.vlan_tag == external_interface.vlan_tag
   end
   return false
end

function run(args)
   local opts, scheduling, conf_file, v4, v6 = parse_args(args)
   local conf = require('apps.lwaftr.conf').load_lwaftr_config(conf_file)
   local use_splitter = requires_splitter(opts, conf)

   -- If there is a name defined on the command line, it should override
   -- anything defined in the config.
   if opts.name then
      conf.softwire_config.name = opts.name
   end

   local c = config.new()
   local setup_fn, setup_args
   if opts.virtio_net then
      setup_fn, setup_args = setup.load_virt, { 'inetNic', v4, 'b4sideNic', v6 }
   elseif opts["on-a-stick"] then
      setup_fn = setup.load_on_a_stick
      setup_args =
         { { v4_nic_name = 'inetNic', v6_nic_name = 'b4sideNic',
             v4v6 = use_splitter and 'v4v6', pciaddr = v4,
             mirror = opts.mirror } }
   else
      setup_fn, setup_args = setup.load_phy, { 'inetNic', v4, 'b4sideNic', v6 }
   end

   if opts.reconfigurable then
      setup.reconfigurable(scheduling, setup_fn, c, conf, unpack(setup_args))
   else
      setup.apply_scheduling(scheduling)
      setup.validate_config(conf)
      setup_fn(c, conf, unpack(setup_args))
   end

   engine.configure(c)

   if opts.verbosity >= 2 then
      local function lnicui_info() engine.report_apps() end
      local t = timer.new("report", lnicui_info, 1e9, 'repeating')
      timer.activate(t)
   end

   if opts.verbosity >= 1 then
      function add_csv_stats_for_pid(pid)
         local csv = csv_stats.CSVStatsTimer:new(opts.bench_file, opts.hydra, pid)
         -- Link names like "tx" are from the app's perspective, but
         -- these labels are from the perspective of the lwAFTR as a
{'fraction_non_alphanumeric': 0.1178690009055237, 'fraction_numerical': 0.011469966797464533, 'mean_word_length': 2.932640949554896, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5977597', 'n_tokens_mistral': 4480, 'n_tokens_neox': 4345, 'n_words': 1192}