Skip to content

Commit

Permalink
Fixed multicast bin and started oo-reorg for multiple model support
Browse files Browse the repository at this point in the history
I think I can improve the multicast filter some more, but I kind
like keeping around the idea of treating different sorts of
multicast differently.

I'd like this script to become a driver for multiple AQM models
in the long run - sfq, qfq, htb+sfq, htb+qfq, netem, etc

Started that migration here... but want it to work on openwrt
next....
  • Loading branch information
Dave Taht committed Dec 27, 2011
1 parent 41f8518 commit 048091b
Showing 1 changed file with 92 additions and 87 deletions.
179 changes: 92 additions & 87 deletions src/staqfq.lua
Expand Up @@ -66,6 +66,7 @@ ETHTOOL='/sbin/ethtool'
-- FIXME - use modprobe on linux, insmod on openwrt

INSMOD='/sbin/modprobe'
QMODEL='qfq'

PREREQS = { 'sch_qfq', 'cls_u32', 'cls_flow' }

Expand All @@ -84,6 +85,7 @@ if os.getenv("BINS") ~= nil then BINS=os.getenv("BINS") end
if os.getenv("MAX_HWQ_BYTES") ~= nil then MAX_HWQ_BYTES=os.getenv("MAX_HWQ_BYTES") end
if os.getenv("ETHTOOL") ~= nil then ETHTOOL=os.getenv("ETHTOOL") end
if os.getenv("NATTED") ~= nil then NATTED=os.getenv("NATTED") end
if os.getenv("QMODEL") ~= nil then QMODEL=os.getenv("QMODEL") end

-- Maltreat multicast especially. When handed to a load balancing
-- filter based on IPs, multicast addresses are all over the map.
Expand All @@ -103,6 +105,68 @@ MULTICAST=BINS+1

DEFAULTB=BINS+2

-- Some utility functions

function file_exists(name)
local f=io.open(name,"r")
if f~=nil then io.close(f) return true else return false end
end

-- FIXME: quiet the warnings

function kernel_prereqs(prereqs)
for i,v in ipairs(prereqs) do
os.execute(string.format("%s %s",INSMOD,v))
end
end

-- can't depend on 'wlan or eth' patterns, so try sysfs
-- FIXME: This needs to be made smarter and detect other forms
-- of tunnel.

function interface_type(iface)
if iface == 'lo' then return('localhost') end
if string.sub(iface,1,3) == 'ifb' then return('ifb') end
-- if string.find(iface,'.') ~= nil then return('vlan') end syntax issue fixme
if string.sub(iface,1,3) == 'gre' then return('tunnel') end
if string.sub(iface,1,2) == 'br' then return('bridge') end
if file_exists(string.format("/sys/class/net/%s/phy80211/name",iface)) then return ('wireless') end
return ('ethernet')
end

-- Under most workloads there doesn't seem to be a need
-- to reduce txqueuelen. Reducing the bql tx ring to 64
-- along with a byte limit of 4500 gives a nice symmetry:
-- 60+ ACKS or 3 big packets.

-- TSO does terrible things to the scheduler
-- GSO does as well
-- UFO is not a feature of most devices

local function ethernet_setup(iface)
os.execute(string.format("%s -G %s tx 64",ETHTOOL,iface))
os.execute(string.format("%s -K %s gso off",ETHTOOL,iface))
os.execute(string.format("%s -K %s tso off",ETHTOOL,iface))
os.execute(string.format("%s -K %s ufo off",ETHTOOL,iface))
-- for testing, limit ethernet to 100Mbit
os.execute(string.format("%s -s %s advertise 0x008",ETHTOOL,iface))
end

-- FIXME: Handle multi queued interfaces

local function bql_setup(iface)
local f = io.open(string.format("/sys/class/net/%s/queues/tx-0/byte_queue_limits/limit_max",iface),'w')
if f ~= nil then
f:write(string.format("%d",MAX_HWQ_BYTES))
f:close()
else
print("Your system does not support byte queue limits")
end
end

-- if type(arg) == 'table' foreach arg self(arg)
-- Some TC helpers

-- TC tends to be repetitive and hard to read
-- So this shortens things considerably by doing
-- the "{class,qdisc,filter} add dev %s" for us
Expand All @@ -125,20 +189,19 @@ local function qa(...)
return tc:write(string.format(qastring,string.format(...)))
end

-- Create a bin attached to the parent class
-- QFQ: Create a bin attached to the parent class

local function cb(base,bin,disc)
ca("parent %x classid %x:%x qfq",base,base,bin)
qa("parent %x:%x %s",base,bin,disc)
end

-- FIXME: It would be nice to have a cleaner way to match all multicast
-- Not, incidentally, that this actually works.

local function fa_mcast(parent)
fa("protocol ip parent %x: prio 5 u32 match u16 0x0100 0x0100 at -14 flowid %x:%x",parent,parent,MULTICAST)
fa("protocol ipv6 parent %x: prio 6 u32 match u16 0x0100 0x0100 at -14 flowid %x:%x",parent,parent,MULTICAST)
fa("protocol arp parent %x: prio 7 u32 match u16 0x0100 0x0100 at -14 flowid %x:%x",parent,parent,MULTICAST)
fa("protocol ip parent %x: prio 5 u32 match u8 0x01 0x01 at -14 flowid %x:%x",parent,parent,MULTICAST)
fa("protocol ipv6 parent %x: prio 6 u32 match u8 0x01 0x01 at -14 flowid %x:%x",parent,parent,MULTICAST)
fa("protocol arp parent %x: prio 7 u32 match u8 0x01 0x01 at -14 flowid %x:%x",parent,parent,MULTICAST)
end

local function fa_defb(parent)
Expand Down Expand Up @@ -166,7 +229,27 @@ local function q_bins(parent)
end
end


-- FIXME: add HTB rate limiter support for a hm gateway
-- What we want are various models expressed object orientedly
-- so we can tie them together eventually

local function model_qfq_pfifo_fast(base)
cb(base,MULTICAST,MDISC)
cb(base,DEFAULTB,NORMDISC)
fa_defb(base)
fa_mcast(base);
q_bins(base);
fa_bins(base);
end

local function model_sfq(base)
qa("parent %x sfq",base)
end

-- Wireless devices are multi-queued
-- recursion would be better and if we can get away from globals
-- we can make it possible to do red, etc

local function wireless()
VO=0x10; VI=0x20; BE=0x30; BK=0x40
Expand All @@ -182,93 +265,15 @@ local function wireless()
-- and into the VO queue. Always. Somehow.-

for i,v in ipairs(QUEUES) do
cb(v,MULTICAST,MDISC)
cb(v,DEFAULTB,NORMDISC)
fa_defb(v)
fa_mcast(v)
-- Build tree
q_bins(v)
-- turn on filters
fa_bins(v)
end
end

-- Under most workloads there doesn't seem to be a need
-- to reduce txqueuelen. Reducing the bql tx ring to 64
-- along with a byte limit of 4500 gives a nice symmetry:
-- 60+ ACKS or 3 big packets.

-- TSO does terrible things to the scheduler
-- GSO does as well
-- UFO is not a feature of most devices

local function ethernet_setup(iface)
os.execute(string.format("%s -G %s tx 64",ETHTOOL,iface))
os.execute(string.format("%s -K %s gso off",ETHTOOL,iface))
os.execute(string.format("%s -K %s tso off",ETHTOOL,iface))
os.execute(string.format("%s -K %s ufo off",ETHTOOL,iface))
-- for testing, limit ethernet to 100Mbit
os.execute(string.format("%s -s %s advertise 0x008",ETHTOOL,iface))
end

-- FIXME: Handle multi queued interfaces

local function bql_setup(iface)
local f = io.open(string.format("/sys/class/net/%s/queues/tx-0/byte_queue_limits/limit_max",iface),'w')
if f ~= nil then
f:write(string.format("%d",MAX_HWQ_BYTES))
f:close()
else
print("Your system does not support byte queue limits")
model_qfq_pfifo_fast(v)
end
end

-- FIXME: add HTB rate limiter support for a hm gateway

local function ethernet()
ethernet_setup(IFACE)
bql_setup(IFACE)
BASE=10
qa("handle %x root qfq",BASE)

-- Treat multicast specially

cb(BASE,MULTICAST,MDISC)
cb(BASE,DEFAULTB,NORMDISC)
fa_defb(BASE)
fa_mcast(BASE);
q_bins(BASE);

-- Turn on filters
fa_bins(BASE);

end

function file_exists(name)
local f=io.open(name,"r")
if f~=nil then io.close(f) return true else return false end
end

-- FIXME: quiet the warnings

function kernel_prereqs()
for i,v in ipairs(PREREQS) do
os.execute(string.format("%s %s",INSMOD,v))
end
end

-- can't depend on 'wlan or eth' patterns, so try sysfs
-- FIXME: This needs to be made smarter and detect other forms
-- of tunnel.

function interface_type(iface)
if iface == 'lo' then return('localhost') end
if string.sub(iface,1,3) == 'ifb' then return('ifb') end
-- if string.find(iface,'.') ~= nil then return('vlan') end syntax issue fixme
if string.sub(iface,1,3) == 'gre' then return('tunnel') end
if string.sub(iface,1,2) == 'br' then return('bridge') end
if file_exists(string.format("/sys/class/net/%s/phy80211/name",iface)) then return ('wireless') end
return ('ethernet')
qa("handle %x root qfq",10)
model_qfq_pfifo_fast(10)
end

-- And away we go
Expand All @@ -277,7 +282,7 @@ end
itype=interface_type(IFACE)

if itype == 'wireless' or itype == 'ethernet' then
kernel_prereqs()
kernel_prereqs(PREREQS)
os.execute(string.format("tc qdisc del dev %s root",IFACE))
tc=io.popen(TC,'w')
if itype == 'wireless' then wireless() end
Expand Down

0 comments on commit 048091b

Please sign in to comment.