Reorganize CPULimitedHost and add cgroup cleanup.
This commit is contained in:
+48
-37
@@ -53,8 +53,6 @@ from mininet.util import numCores
|
|||||||
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
|
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
|
||||||
from mininet.link import Link
|
from mininet.link import Link
|
||||||
|
|
||||||
SWITCH_PORT_BASE = 1 # For OF > 0.9, switch ports start at 1 rather than zero
|
|
||||||
|
|
||||||
class Node( object ):
|
class Node( object ):
|
||||||
"""A virtual network node is simply a shell in a network namespace.
|
"""A virtual network node is simply a shell in a network namespace.
|
||||||
We communicate with it using pipes."""
|
We communicate with it using pipes."""
|
||||||
@@ -482,17 +480,28 @@ class CPULimitedHost( Host ):
|
|||||||
def __init__( self, *args, **kwargs ):
|
def __init__( self, *args, **kwargs ):
|
||||||
Node.__init__( self, *args, **kwargs )
|
Node.__init__( self, *args, **kwargs )
|
||||||
# Create a cgroup and move shell into it
|
# Create a cgroup and move shell into it
|
||||||
cgroup = 'cpu,cpuacct:/' + self.name
|
self.cgroup = 'cpu,cpuacct:/' + self.name
|
||||||
errFail( 'cgcreate -g ' + cgroup )
|
errFail( 'cgcreate -g ' + self.cgroup )
|
||||||
errFail( 'cgclassify -g %s %s' % ( cgroup, self.pid ) )
|
errFail( 'cgclassify -g %s %s' % ( self.cgroup, self.pid ) )
|
||||||
self.period_us = kwargs.get( 'period_us', 10000 )
|
self.period_us = kwargs.get( 'period_us', 10000 )
|
||||||
self.sched = kwargs.get( 'sched', 'rt' )
|
self.sched = kwargs.get( 'sched', 'rt' )
|
||||||
|
|
||||||
|
def cleanup( self ):
|
||||||
|
"Clean up our cgroup"
|
||||||
|
Host.cleanup( self )
|
||||||
|
debug( '*** deleting cgroup', self.cgroup, '\n' )
|
||||||
|
errFail( 'cgdelete -r ' + self.cgroup )
|
||||||
|
|
||||||
def cgroupSet( self, param, value, resource='cpu' ):
|
def cgroupSet( self, param, value, resource='cpu' ):
|
||||||
"Set a cgroup parameter and return its value"
|
"Set a cgroup parameter and return its value"
|
||||||
cmd = 'cgset -r %s.%s=%s /%s' % (
|
cmd = 'cgset -r %s.%s=%s /%s' % (
|
||||||
resource, param, value, self.name )
|
resource, param, value, self.name )
|
||||||
return quietRun( cmd )
|
out = quietRun( cmd )
|
||||||
|
nvalue = int( self.cgroupGet( param, resource ) )
|
||||||
|
if nvalue != value:
|
||||||
|
error( '*** error: cgroupSet: %s set to %s instead of %s\n'
|
||||||
|
% ( param, nvalue, value ) )
|
||||||
|
return nvalue
|
||||||
|
|
||||||
def cgroupGet( self, param, resource='cpu' ):
|
def cgroupGet( self, param, resource='cpu' ):
|
||||||
cmd = 'cgget -r %s.%s /%s' % (
|
cmd = 'cgget -r %s.%s /%s' % (
|
||||||
@@ -505,8 +514,29 @@ class CPULimitedHost( Host ):
|
|||||||
result = quietRun( 'chrt -p %s' % self.pid )
|
result = quietRun( 'chrt -p %s' % self.pid )
|
||||||
firstline = result.split( '\n' )[ 0 ]
|
firstline = result.split( '\n' )[ 0 ]
|
||||||
lastword = firstline.split( ' ' )[ -1 ]
|
lastword = firstline.split( ' ' )[ -1 ]
|
||||||
|
if lastword != 'SCHED_RR':
|
||||||
|
error( '*** error: could not assign SCHED_RR to %s\n' % self.name )
|
||||||
return lastword
|
return lastword
|
||||||
|
|
||||||
|
def rtInfo( self, f ):
|
||||||
|
"Internal method: return parameters for RT bandwidth"
|
||||||
|
pstr, qstr = 'rt_period_us', 'rt_runtime_us'
|
||||||
|
# RT uses wall clock time for period and quota
|
||||||
|
quota = int( self.period_us * f * numCores() )
|
||||||
|
return pstr, qstr, self.period_us, quota
|
||||||
|
|
||||||
|
def cfsInfo( self, f):
|
||||||
|
"Internal method: return parameters for CFS bandwidth"
|
||||||
|
pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
|
||||||
|
# CFS uses wall clock time for period and CPU time for quota.
|
||||||
|
quota = int( self.period_us * f * numCores() )
|
||||||
|
period = self.period_us
|
||||||
|
if f > 0 and quota < 1000:
|
||||||
|
debug( '(cfsInfo: increasing default period) ' )
|
||||||
|
quota = 1000
|
||||||
|
period = int( quota / f / numCores() )
|
||||||
|
return pstr, qstr, period, quota
|
||||||
|
|
||||||
# BL comment:
|
# BL comment:
|
||||||
# This may not be the right API,
|
# This may not be the right API,
|
||||||
# since it doesn't specify CPU bandwidth in "absolute"
|
# since it doesn't specify CPU bandwidth in "absolute"
|
||||||
@@ -515,7 +545,7 @@ class CPULimitedHost( Host ):
|
|||||||
# Alternatively, we should change from system fraction
|
# Alternatively, we should change from system fraction
|
||||||
# to CPU seconds per second, essentially assuming that
|
# to CPU seconds per second, essentially assuming that
|
||||||
# all CPUs are the same.
|
# all CPUs are the same.
|
||||||
|
|
||||||
def setCPUFrac( self, f=-1, sched=None):
|
def setCPUFrac( self, f=-1, sched=None):
|
||||||
"""Set overall CPU fraction for this host
|
"""Set overall CPU fraction for this host
|
||||||
f: CPU bandwidth limit (fraction)
|
f: CPU bandwidth limit (fraction)
|
||||||
@@ -525,45 +555,22 @@ class CPULimitedHost( Host ):
|
|||||||
return
|
return
|
||||||
if not sched:
|
if not sched:
|
||||||
sched = self.sched
|
sched = self.sched
|
||||||
period = self.period_us
|
|
||||||
if sched == 'rt':
|
if sched == 'rt':
|
||||||
pstr, qstr = 'rt_period_us', 'rt_runtime_us'
|
pstr, qstr, period, quota = self.rtInfo( f )
|
||||||
# RT uses system time for period and quota
|
|
||||||
quota = int( period * f * numCores() )
|
|
||||||
elif sched == 'cfs':
|
elif sched == 'cfs':
|
||||||
pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
|
pstr, qstr, period, quota = self.cfsInfo( f )
|
||||||
# CFS uses wall clock time for period and CPU time for quota.
|
|
||||||
quota = int( self.period_us * f * numCores() )
|
|
||||||
if f > 0 and quota < 1000:
|
|
||||||
info( '*** setCPUFrac: quota too small - adjusting period\n' )
|
|
||||||
quota = 1000
|
|
||||||
period = int( quota / f / numCores() )
|
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
if quota < 0:
|
if quota < 0:
|
||||||
# Reset to unlimited
|
# Reset to unlimited
|
||||||
quota = -1
|
quota = -1
|
||||||
# Set cgroup's period and quota
|
# Set cgroup's period and quota
|
||||||
self.cgroupSet( pstr, period )
|
nperiod = self.cgroupSet( pstr, period )
|
||||||
nquota = int ( self.cgroupGet( qstr ) )
|
nquota = self.cgroupSet( qstr, quota )
|
||||||
self.cgroupSet( qstr, quota )
|
|
||||||
nperiod = int( self.cgroupGet( pstr ) )
|
|
||||||
# Make sure it worked
|
|
||||||
if nperiod != self.period_us:
|
|
||||||
error( '*** error: period is %s rather than %s\n' % (
|
|
||||||
nperiod, self.period_us ) )
|
|
||||||
if nquota != quota:
|
|
||||||
error( '*** error: quota is %s rather than %s\n' % (
|
|
||||||
nquota, quota ) )
|
|
||||||
if sched == 'rt':
|
if sched == 'rt':
|
||||||
# Set RT priority if necessary
|
# Set RT priority if necessary
|
||||||
nchrt = self.chrt( prio=20 )
|
nchrt = self.chrt( prio=20 )
|
||||||
# Nake sure it worked
|
info( '(%s %d/%dus) ' % ( sched, quota, period ) )
|
||||||
if sched == 'SCHED_RR' not in nchrt:
|
|
||||||
error( '*** error: could not assign SCHED_RR to %s\n' % self.name )
|
|
||||||
info( '( period', nperiod, 'quota', nquota, nchrt, ') ' )
|
|
||||||
else:
|
|
||||||
info( '( period', nperiod, 'quota', nquota, ') ' )
|
|
||||||
|
|
||||||
def config( self, cpu=None, sched=None, **params ):
|
def config( self, cpu=None, sched=None, **params ):
|
||||||
"""cpu: desired overall system CPU fraction
|
"""cpu: desired overall system CPU fraction
|
||||||
@@ -591,16 +598,20 @@ class CPULimitedHost( Host ):
|
|||||||
# reported by ifconfig for a switch data port is essentially
|
# reported by ifconfig for a switch data port is essentially
|
||||||
# meaningless.
|
# meaningless.
|
||||||
#
|
#
|
||||||
# So, I'm tyring changing the API to make it
|
# So, I'm trying changing the API to make it
|
||||||
# impossible to try this, since it will not work, since nobody
|
# impossible to try this, since it will not work, since nobody
|
||||||
# ever makes separate control networks in Mininet, and indeed
|
# ever makes separate control networks in Mininet, and indeed
|
||||||
# we don't even support running OVS in a namespace.
|
# we don't even support running OVS in a namespace.
|
||||||
|
#
|
||||||
|
# Note if we have a separate control network, then it does
|
||||||
|
# make sense to have s1-eth0 as s1's control network interface,
|
||||||
|
# and we should set controlIntf accordingly.
|
||||||
|
|
||||||
class Switch( Node ):
|
class Switch( Node ):
|
||||||
"""A Switch is a Node that is running (or has execed?)
|
"""A Switch is a Node that is running (or has execed?)
|
||||||
an OpenFlow switch."""
|
an OpenFlow switch."""
|
||||||
|
|
||||||
portBase = SWITCH_PORT_BASE # 0 for OF < 1.0, 1 for OF >= 1.0
|
portBase = 1 # Switches start with port 1 in OpenFlow
|
||||||
|
|
||||||
def __init__( self, name, dpid=None, opts='', listenPort=None, **params):
|
def __init__( self, name, dpid=None, opts='', listenPort=None, **params):
|
||||||
"""dpid: dpid for switch (or None for default)
|
"""dpid: dpid for switch (or None for default)
|
||||||
|
|||||||
Reference in New Issue
Block a user