Merge branch 'dev' into fs-sw-docs

This commit is contained in:
Nathan Pemberton 2018-11-09 21:41:55 -05:00
commit df975d50d3
18 changed files with 582 additions and 121 deletions

View File

@ -20,9 +20,11 @@ class FireSimTopology(UserTopologies):
if nextup in visitedonce:
if nextup not in retlist:
retlist.append(stack.pop(0))
else:
stack.pop(0)
else:
visitedonce.add(nextup)
stack = nextup.downlinks + stack
stack = list(map(lambda x: x.get_downlink_side(), nextup.downlinks)) + stack
return retlist
def get_dfs_order_switches(self):
@ -41,6 +43,10 @@ class FireSimTopology(UserTopologies):
def __init__(self, user_topology_name, no_net_num_nodes):
# This just constructs the user topology. an upper level pass manager
# will apply passes to it.
# a topology can specify a custom target -> host mapping. if left as None,
# the default mapper is used, which handles no network and simple networked cases.
self.custom_mapper = None
self.no_net_num_nodes = no_net_num_nodes
configfunc = getattr(self, user_topology_name)
configfunc()

View File

@ -8,6 +8,77 @@ from fabric.api import *
rootLogger = logging.getLogger()
class FireSimLink(object):
""" This represents a link that connects different FireSimNodes.
Terms:
Naming assumes a tree-ish topology, with roots at the top, leaves at the
bottom. So in a topology like:
RootSwitch
/ \
Link A / \ Link B
/ \
Sim X Sim Y
"Uplink side" of Link A is RootSwitch.
"Downlink side" of Link A is Sim X.
Sim X has an uplink connected to RootSwitch.
RootSwitch has a downlink to Sim X.
"""
# links have a globally unique identifier, currently used for naming
# shmem regions for Shmem Links
next_unique_link_identifier = 0
def __init__(self, uplink_side, downlink_side):
self.id = FireSimLink.next_unique_link_identifier
FireSimLink.next_unique_link_identifier += 1
# format as 100 char hex string padded with zeroes
self.id_as_str = format(self.id, '0100X')
self.uplink_side = None
self.downlink_side = None
self.port = None
self.set_uplink_side(uplink_side)
self.set_downlink_side(downlink_side)
def set_uplink_side(self, fsimnode):
self.uplink_side = fsimnode
def set_downlink_side(self, fsimnode):
self.downlink_side = fsimnode
def get_uplink_side(self):
return self.uplink_side
def get_downlink_side(self):
return self.downlink_side
def link_hostserver_port(self):
""" Get the port used for this Link. This should only be called for
links implemented with SocketPorts. """
if self.port is None:
self.port = self.get_uplink_side().host_instance.allocate_host_port()
return self.port
def link_hostserver_ip(self):
""" Get the IP address used for this Link. This should only be called for
links implemented with SocketPorts. """
assert self.get_uplink_side().host_instance.is_bound_to_real_instance(), "Instances must be bound to private IP to emit switches with uplinks. i.e. you must have a running Run Farm."
return self.get_uplink_side().host_instance.get_private_ip()
def link_crosses_hosts(self):
""" Return True if the user has mapped the two endpoints of this link to
separate hosts. This implies a SocketServerPort / SocketClientPort will be used
to implement the Link. If False, use a sharedmem port to implement the link. """
return self.get_uplink_side().host_instance != self.get_downlink_side().host_instance
def get_global_link_id(self):
""" Return the globally unique link id, used for naming shmem ports. """
return self.id_as_str
class FireSimNode(object):
""" This represents a node in the high-level FireSim Simulation Topology
Graph. These nodes are either
@ -28,6 +99,8 @@ class FireSimNode(object):
def __init__(self):
self.downlinks = []
# used when there are multiple links between switches to disambiguate
#self.downlinks_consumed = []
self.uplinks = []
self.host_instance = None
@ -35,21 +108,23 @@ class FireSimNode(object):
""" A "downlink" is a link that will take you further from the root
of the tree. Users define a tree topology by specifying "downlinks".
Uplinks are automatically inferred. """
firesimnode.add_uplink(self)
self.downlinks.append(firesimnode)
linkobj = FireSimLink(self, firesimnode)
firesimnode.add_uplink(linkobj)
self.downlinks.append(linkobj)
#self.downlinks_consumed.append(False)
def add_downlinks(self, firesimnodes):
""" Just a convenience function to add multiple downlinks at once.
Assumes downlinks in the supplied list are ordered. """
[self.add_downlink(node) for node in firesimnodes]
def add_uplink(self, firesimnode):
def add_uplink(self, firesimlink):
""" This is only for internal use - uplinks are automatically populated
when a node is specified as the downlink of another.
An "uplink" is a link that takes you towards one of the roots of the
tree."""
self.uplinks.append(firesimnode)
self.uplinks.append(firesimlink)
def num_links(self):
""" Return the total number of nodes. """
@ -112,10 +187,15 @@ class FireSimServerNode(FireSimNode):
""" return the command to start the simulation. assumes it will be
called in a directory where its required_files are already located.
"""
shmemportname = "default"
if self.uplinks:
shmemportname = self.uplinks[0].get_global_link_id()
return self.server_hardware_config.get_boot_simulation_command(
self.get_mac_address(), self.get_rootfs_name(), slotno,
self.server_link_latency, self.server_bw_max, self.get_bootbin_name(),
self.trace_enable, self.trace_start, self.trace_end)
self.trace_enable, self.trace_start, self.trace_end, shmemportname)
def copy_back_job_results_from_run(self, slotno):
"""

View File

@ -11,11 +11,10 @@ from firesim_topology_core import *
from utils import MacAddress
from fabric.api import *
from colorama import Fore, Style
import types
from util.streamlogger import StreamLogger
#BASEPORT = 10000
rootLogger = logging.getLogger()
@parallel
@ -96,7 +95,7 @@ class FireSimTopologyWithPasses:
if isinstance(node, FireSimServerNode):
node.downlinkmacs = [node.get_mac_address()]
else:
childdownlinkmacs = [x.downlinkmacs for x in node.downlinks]
childdownlinkmacs = [x.get_downlink_side().downlinkmacs for x in node.downlinks]
node.downlinkmacs = reduce(lambda x, y: x + y, childdownlinkmacs)
switches_dfs_order = self.firesimtopol.get_dfs_order_switches()
@ -107,7 +106,7 @@ class FireSimTopologyWithPasses:
# prepopulate the table with the last port, which will be
switchtab = [uplinkportno for x in range(MacAddress.next_mac_to_allocate())]
for port_no in range(len(switch.downlinks)):
portmacs = switch.downlinks[port_no].downlinkmacs
portmacs = switch.downlinks[port_no].get_downlink_side().downlinkmacs
for mac in portmacs:
switchtab[mac.as_int_no_prefix()] = port_no
@ -136,6 +135,7 @@ class FireSimTopologyWithPasses:
switches_dfs_order = self.firesimtopol.get_dfs_order_switches()
for node in switches_dfs_order:
for downlink in node.downlinks:
downlink = downlink.get_downlink_side()
gviz_graph.edge(str(node), str(downlink))
gviz_graph.render(view=False)
@ -169,24 +169,41 @@ class FireSimTopologyWithPasses:
m4_16s_used = 0
for switch in switches:
if all([isinstance(x, FireSimSwitchNode) for x in switch.downlinks]):
downlinknodes = map(lambda x: x.get_downlink_side(), switch.downlinks)
if all([isinstance(x, FireSimSwitchNode) for x in downlinknodes]):
# all downlinks are switches
self.run_farm.m4_16s[m4_16s_used].add_switch(switch)
m4_16s_used += 1
elif all([isinstance(x, FireSimServerNode) for x in switch.downlinks]):
elif all([isinstance(x, FireSimServerNode) for x in downlinknodes]):
# all downlinks are simulations
if (len(switch.downlinks) == 1) and (f1_2s_used < len(self.run_farm.f1_2s)):
self.run_farm.f1_2s[f1_2s_used].add_switch(switch)
self.run_farm.f1_2s[f1_2s_used].add_simulation(switch.downlinks[0])
self.run_farm.f1_2s[f1_2s_used].add_simulation(downlinknodes[0])
f1_2s_used += 1
else:
self.run_farm.f1_16s[f1_16s_used].add_switch(switch)
for server in switch.downlinks:
for server in downlinknodes:
self.run_farm.f1_16s[f1_16s_used].add_simulation(server)
f1_16s_used += 1
else:
assert False, "Mixed downlinks currently not supported."""
def mapping_use_one_f1_16xlarge(self):
""" Just put everything on one f1.16xlarge """
switches = self.firesimtopol.get_dfs_order_switches()
f1_2s_used = 0
f1_16s_used = 0
m4_16s_used = 0
for switch in switches:
self.run_farm.f1_16s[f1_16s_used].add_switch(switch)
downlinknodes = map(lambda x: x.get_downlink_side(), switch.downlinks)
if all([isinstance(x, FireSimServerNode) for x in downlinknodes]):
for server in downlinknodes:
self.run_farm.f1_16s[f1_16s_used].add_simulation(server)
elif any([isinstance(x, FireSimServerNode) for x in downlinknodes]):
assert False, "MIXED DOWNLINKS NOT SUPPORTED."
f1_16s_used += 1
def pass_perform_host_node_mapping(self):
""" This pass assigns host nodes to nodes in the abstract FireSim
@ -197,18 +214,30 @@ class FireSimTopologyWithPasses:
top level elements are switches, it will assume you're simulating a
networked config, """
# if your roots are servers, just pack as tightly as possible, since
# you have no_net_config
if all([isinstance(x, FireSimServerNode) for x in self.firesimtopol.roots]):
# all roots are servers, so we're in no_net_config
# if the user has specified any 16xlarges, we assign to them first
self.pass_no_net_host_mapping()
return
# now, we're handling the cycle-accurate networked simulation case
# currently, we only handle the case where
self.pass_simple_networked_host_node_mapping()
if self.firesimtopol.custom_mapper is None:
""" Use default mapping strategy. The topol has not specified a
special one. """
# if your roots are servers, just pack as tightly as possible, since
# you have no_net_config
if all([isinstance(x, FireSimServerNode) for x in self.firesimtopol.roots]):
# all roots are servers, so we're in no_net_config
# if the user has specified any 16xlarges, we assign to them first
self.pass_no_net_host_mapping()
return
else:
# now, we're handling the cycle-accurate networked simulation case
# currently, we only handle the case where
self.pass_simple_networked_host_node_mapping()
elif type(self.firesimtopol.custom_mapper) == types.FunctionType:
""" call the mapper fn defined in the topology itself. """
self.firesimtopol.custom_mapper(self)
elif type(self.firesimtopol.custom_mapper) == str:
""" assume that the mapping strategy is a custom pre-defined strategy
given in this class, supplied as a string in the topology """
mapperfunc = getattr(self, self.firesimtopol.custom_mapper)
mapperfunc()
else:
assert False, "IMPROPER MAPPING CONFIGURATION"
def pass_apply_default_hwconfig(self):
""" This is the default mapping pass for hardware configurations - it
@ -270,7 +299,7 @@ class FireSimTopologyWithPasses:
automatically when creating this object. """
self.pass_assign_mac_addresses()
self.pass_compute_switching_tables()
self.pass_perform_host_node_mapping()
self.pass_perform_host_node_mapping() # TODO: we can know ports here?
self.pass_apply_default_hwconfig()
self.pass_apply_default_network_params()
self.pass_assign_jobs()

View File

@ -22,15 +22,17 @@ class MockBoto3Instance:
self.private_ip_address = ".".join([str((self.ip_addr_int >> (8*x)) & 0xFF) for x in [3, 2, 1, 0]])
class EC2Inst(object):
# for now, we require that only one switch is mapped to each host
# this could be overridden based on instance type later
SWITCH_SLOTS = 1
# TODO: this is leftover from when we could only support switch slots.
# This can be removed once self.switch_slots is dynamically allocated.
# Just make it arbitrarily large for now.
SWITCH_SLOTS = 100000
def __init__(self):
self.boto3_instance_object = None
self.switch_slots = [None for x in range(self.SWITCH_SLOTS)]
self.switch_slots_consumed = 0
self.instance_deploy_manager = InstanceDeployManager(self)
self._next_port = 10000 # track ports to allocate for server switch model ports
def assign_boto3_instance_object(self, boto3obj):
self.boto3_instance_object = boto3obj
@ -48,8 +50,16 @@ class EC2Inst(object):
firesimswitchnode.assign_host_instance(self)
self.switch_slots_consumed += 1
def get_num_switch_slots(self):
return self.SWITCH_SLOTS
def get_num_switch_slots_consumed(self):
return self.switch_slots_consumed
def allocate_host_port(self):
""" Allocate a port to use for something on the host. Successive calls
will return a new port. """
retport = self._next_port
assert retport < 11000, "Exceeded number of ports used on host. You will need to modify your security groups to increase this value."
self._next_port += 1
return retport
class F1_Instance(EC2Inst):
FPGA_SLOTS = 0
@ -327,7 +337,11 @@ class InstanceDeployManager:
for slotno in range(self.parentnode.get_num_fpga_slots_max()):
self.instance_logger("""Clearing FPGA Slot {}.""".format(slotno))
with StreamLogger('stdout'), StreamLogger('stderr'):
run("""sudo fpga-clear-local-image -S {}""".format(slotno))
run("""sudo fpga-clear-local-image -S {} -A""".format(slotno))
for slotno in range(self.parentnode.get_num_fpga_slots_max()):
self.instance_logger("""Checking for Cleared FPGA Slot {}.""".format(slotno))
with StreamLogger('stdout'), StreamLogger('stderr'):
run("""until sudo fpga-describe-local-image -S {} -R -H | grep -q "cleared"; do sleep 1; done""".format(slotno))
def flash_fpgas(self):
for firesimservernode, slotno in zip(self.parentnode.fpga_slots, range(self.parentnode.get_num_fpga_slots_consumed())):
@ -335,8 +349,13 @@ class InstanceDeployManager:
agfi = firesimservernode.get_agfi()
self.instance_logger("""Flashing FPGA Slot: {} with agfi: {}.""".format(slotno, agfi))
with StreamLogger('stdout'), StreamLogger('stderr'):
run("""sudo fpga-load-local-image -S {} -I {}""".format(
run("""sudo fpga-load-local-image -S {} -I {} -A""".format(
slotno, agfi))
for firesimservernode, slotno in zip(self.parentnode.fpga_slots, range(self.parentnode.get_num_fpga_slots_consumed())):
if firesimservernode is not None:
self.instance_logger("""Checking for Flashed FPGA Slot: {} with agfi: {}.""".format(slotno, agfi))
with StreamLogger('stdout'), StreamLogger('stderr'):
run("""until sudo fpga-describe-local-image -S {} -R -H | grep -q "loaded"; do sleep 1; done""".format(slotno))
def load_edma(self):
""" load the edma kernel module. """
@ -378,6 +397,7 @@ class InstanceDeployManager:
files_to_copy = serv.get_required_files_local_paths()
for filename in files_to_copy:
with StreamLogger('stdout'), StreamLogger('stderr'):
# -z --inplace
rsync_cap = rsync_project(local_dir=filename, remote_dir=remote_sim_rsync_dir,
ssh_opts="-o StrictHostKeyChecking=no", extra_opts="-L", capture=True)
rootLogger.debug(rsync_cap)
@ -469,7 +489,7 @@ class InstanceDeployManager:
if self.instance_assigned_switches():
# all nodes could have a switch
for slotno in range(self.parentnode.get_num_switch_slots()):
for slotno in range(self.parentnode.get_num_switch_slots_consumed()):
self.copy_switch_slot_infrastructure(slotno)
@ -480,7 +500,7 @@ class InstanceDeployManager:
with StreamLogger('stdout'), StreamLogger('stderr'):
run("sudo rm -rf /dev/shm/*")
for slotno in range(self.parentnode.get_num_switch_slots()):
for slotno in range(self.parentnode.get_num_switch_slots_consumed()):
self.start_switch_slot(slotno)
def start_simulations_instance(self):
@ -493,7 +513,7 @@ class InstanceDeployManager:
def kill_switches_instance(self):
""" Kill all the switches on this instance. """
if self.instance_assigned_switches():
for slotno in range(self.parentnode.get_num_switch_slots()):
for slotno in range(self.parentnode.get_num_switch_slots_consumed()):
self.kill_switch_slot(slotno)
with StreamLogger('stdout'), StreamLogger('stderr'):
run("sudo rm -rf /dev/shm/*")
@ -541,7 +561,9 @@ class InstanceDeployManager:
if teardown:
# handle the case where we're just tearing down nodes that have
# ONLY switches
for counter, switchsim in enumerate(self.parentnode.switch_slots):
numswitchesused = self.parentnode.get_num_switch_slots_consumed()
for counter in range(numswitchesused):
switchsim = self.parentnode.switch_slots[counter]
switchsim.copy_back_switchlog_from_run(job_results_dir, counter)
if terminateoncompletion:
@ -555,7 +577,7 @@ class InstanceDeployManager:
# not teardown - just get the status of the switch sims
switchescompleteddict = {k: False for k in self.running_simulations()['switches']}
for switchsim in self.parentnode.switch_slots:
for switchsim in self.parentnode.switch_slots[:self.parentnode.get_num_switch_slots_consumed()]:
swname = switchsim.switch_builder.switch_binary_name()
if swname not in switchescompleteddict.keys():
switchescompleteddict[swname] = True
@ -587,7 +609,7 @@ class InstanceDeployManager:
if self.instance_assigned_switches():
# fill in whether switches have terminated for some reason
for switchsim in self.parentnode.switch_slots:
for switchsim in self.parentnode.switch_slots[:self.parentnode.get_num_switch_slots_consumed()]:
swname = switchsim.switch_builder.switch_binary_name()
if swname not in switchescompleteddict.keys():
switchescompleteddict[swname] = True
@ -626,7 +648,7 @@ class InstanceDeployManager:
self.kill_switches_instance()
for counter, switchsim in enumerate(self.parentnode.switch_slots):
for counter, switchsim in enumerate(self.parentnode.switch_slots[:self.parentnode.get_num_switch_slots_consumed()]):
switchsim.copy_back_switchlog_from_run(job_results_dir, counter)
if now_done and terminateoncompletion:

View File

@ -76,7 +76,7 @@ class RuntimeHWConfig:
def get_boot_simulation_command(self, macaddr, blkdev, slotid,
linklatency, netbw, bootbin,
trace_enable, trace_start, trace_end):
trace_enable, trace_start, trace_end, shmemportname):
""" return the command used to boot the simulation. this has to have
some external params passed to it, because not everything is contained
in a runtimehwconfig. TODO: maybe runtimehwconfig should be renamed to
@ -89,10 +89,10 @@ class RuntimeHWConfig:
# the sed is in there to get rid of newlines in runtime confs
driver = self.get_local_driver_binaryname()
runtimeconf = self.get_local_runtimeconf_binaryname()
basecommand = """screen -S fsim{slotid} -d -m bash -c "script -f -c 'stty intr ^] && sudo ./{driver} +permissive $(sed \':a;N;$!ba;s/\\n/ /g\' {runtimeconf}) +macaddr={macaddr} +blkdev={blkdev} +slotid={slotid} +niclog=niclog {tracefile} +trace-start={trace_start} +trace-end={trace_end} +linklatency={linklatency} +netbw={netbw} +profile-interval=-1 +zero-out-dram +permissive-off {bootbin} && stty intr ^c' uartlog"; sleep 1""".format(
basecommand = """screen -S fsim{slotid} -d -m bash -c "script -f -c 'stty intr ^] && sudo ./{driver} +permissive $(sed \':a;N;$!ba;s/\\n/ /g\' {runtimeconf}) +macaddr={macaddr} +blkdev={blkdev} +slotid={slotid} +niclog=niclog {tracefile} +trace-start={trace_start} +trace-end={trace_end} +linklatency={linklatency} +netbw={netbw} +profile-interval=-1 +zero-out-dram +shmemportname={shmemportname} +permissive-off {bootbin} && stty intr ^c' uartlog"; sleep 1""".format(
slotid=slotid, driver=driver, runtimeconf=runtimeconf,
macaddr=macaddr, blkdev=blkdev, linklatency=linklatency,
netbw=netbw, bootbin=bootbin, tracefile=tracefile,
netbw=netbw, shmemportname=shmemportname, bootbin=bootbin, tracefile=tracefile,
trace_start=trace_start, trace_end=trace_end)
return basecommand

View File

@ -11,8 +11,6 @@ from util.streamlogger import StreamLogger
rootLogger = logging.getLogger()
BASEPORT = 10000
class AbstractSwitchToSwitchConfig:
""" This class is responsible for providing functions that take a FireSimSwitchNode
and emit the correct config header to produce an actual switch simulator binary
@ -28,37 +26,35 @@ class AbstractSwitchToSwitchConfig:
self.build_disambiguate = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(64))
def emit_init_for_uplink(self, uplinkno):
""" Emit an init for a switch to talk to it's uplink.
""" Emit an init for a switch to talk to it's uplink."""
TODO: currently, we only support one uplink. """
assert uplinkno < 1, "Only 1 uplink is currently supported."
downlinkno = None
iterno = 0
for downlink in self.fsimswitchnode.uplinks[uplinkno].downlinks:
if self.fsimswitchnode == downlink:
downlinkno = iterno
break
iterno += 1
assert self.fsimswitchnode.host_instance.is_bound_to_real_instance(), "Instances must be bound to private IP to emit switches with uplinks. i.e. you must have a running Run Farm."
# TODO: remove the requirement from the above assert by passing IPs
# as cmd line arguments.
uplinkip = self.fsimswitchnode.uplinks[uplinkno].host_instance.get_private_ip()
return "new SocketClientPort(" + str(len(self.fsimswitchnode.downlinks)+uplinkno) + \
", \"" + uplinkip + "\", " + str(BASEPORT + downlinkno) + ");\n"
linkobj = self.fsimswitchnode.uplinks[uplinkno]
upperswitch = linkobj.get_uplink_side()
target_local_portno = len(self.fsimswitchnode.downlinks) + uplinkno
if linkobj.link_crosses_hosts():
uplinkhostip = linkobj.link_hostserver_ip() #upperswitch.host_instance.get_private_ip()
uplinkhostport = linkobj.link_hostserver_port()
return "new SocketClientPort(" + str(target_local_portno) + \
", \"" + uplinkhostip + "\", " + str(uplinkhostport) + ");\n"
else:
linkbasename = linkobj.get_global_link_id()
return "new ShmemPort(" + str(target_local_portno) + ', "' + linkbasename + '", true);\n'
def emit_init_for_downlink(self, downlinkno):
""" emit an init for the specified downlink. """
downlink = self.fsimswitchnode.downlinks[downlinkno]
# this must be here to avoid circular deps
# TODO: for real fix, need to refactor the abstract switch / implementation
# interface
from runtools.firesim_topology_elements import FireSimSwitchNode
if isinstance(downlink, FireSimSwitchNode):
downlinkobj = self.fsimswitchnode.downlinks[downlinkno]
downlink = downlinkobj.get_downlink_side()
if downlinkobj.link_crosses_hosts():
hostport = downlinkobj.link_hostserver_port()
# create a SocketServerPort
return "new SocketServerPort(" + str(downlinkno) + ", " + \
str(BASEPORT + downlinkno) + ");\n"
str(hostport) + ");\n"
else:
return "new ShmemPort(" + str(downlinkno) + ");\n"
linkbasename = downlinkobj.get_global_link_id()
return "new ShmemPort(" + str(downlinkno) + ', "' + linkbasename + '", false);\n'
def emit_switch_configfile(self):
""" Produce a config file for the switch generator for this switch """
@ -99,12 +95,16 @@ class AbstractSwitchToSwitchConfig:
def get_numclientsconfig(self):
""" Emit constants for num ports. """
totalports = len(self.fsimswitchnode.downlinks) + len(self.fsimswitchnode.uplinks)
numdownlinks = len(self.fsimswitchnode.downlinks)
numuplinks = len(self.fsimswitchnode.uplinks)
totalports = numdownlinks + numuplinks
retstr = """
#ifdef NUMCLIENTSCONFIG
#define NUMPORTS {}
#endif""".format(totalports)
#define NUMDOWNLINKS {}
#define NUMUPLINKS {}
#endif""".format(totalports, numdownlinks, numuplinks)
return retstr
def get_portsetup(self):
@ -166,7 +166,8 @@ class AbstractSwitchToSwitchConfig:
""" Return the command to boot the switch."""
switchlatency = self.fsimswitchnode.switch_switching_latency
linklatency = self.fsimswitchnode.switch_link_latency
return """screen -S {} -d -m bash -c "script -f -c './{} {} {}' switchlog"; sleep 1""".format(self.switch_binary_name(), self.switch_binary_name(), linklatency, switchlatency)
# insert gdb -ex run --args between sudo and ./ below to start switches in gdb
return """screen -S {} -d -m bash -c "script -f -c 'sudo ./{} {} {}' switchlog"; sleep 1""".format(self.switch_binary_name(), self.switch_binary_name(), linklatency, switchlatency)
def kill_switch_simulation_command(self):
""" Return the command to kill the switch. """

View File

@ -8,6 +8,174 @@ class UserTopologies(object):
""" A class that just separates out user-defined/configurable topologies
from the rest of the boilerplate in FireSimTopology() """
def clos_m_n_r(self, m, n, r):
""" DO NOT USE THIS DIRECTLY, USE ONE OF THE INSTANTIATIONS BELOW. """
""" Clos topol where:
m = number of root switches
n = number of links to nodes on leaf switches
r = number of leaf switches
and each leaf switch has a link to each root switch.
With the default mapping specified below, you will need:
m m4.16xlarges.
n f1.16xlarges.
TODO: improve this later to pack leaf switches with <= 4 downlinks onto
one 16x.large.
"""
rootswitches = [FireSimSwitchNode() for x in range(m)]
self.roots = rootswitches
leafswitches = [FireSimSwitchNode() for x in range(r)]
servers = [[FireSimServerNode() for x in range(n)] for y in range(r)]
for rswitch in rootswitches:
rswitch.add_downlinks(leafswitches)
for leafswitch, servergroup in zip(leafswitches, servers):
leafswitch.add_downlinks(servergroup)
def custom_mapper(fsim_topol_with_passes):
for i, rswitch in enumerate(rootswitches):
fsim_topol_with_passes.run_farm.m4_16s[i].add_switch(rswitch)
for j, lswitch in enumerate(leafswitches):
fsim_topol_with_passes.run_farm.f1_16s[j].add_switch(lswitch)
for sim in servers[j]:
fsim_topol_with_passes.run_farm.f1_16s[j].add_simulation(sim)
self.custom_mapper = custom_mapper
def clos_2_8_2(self):
""" clos topol with:
2 roots
8 nodes/leaf
2 leaves. """
self.clos_m_n_r(2, 8, 2)
def clos_8_8_16(self):
""" clos topol with:
8 roots
8 nodes/leaf
16 leaves. = 128 nodes."""
self.clos_m_n_r(8, 8, 16)
def fat_tree_4ary(self):
# 4-ary fat tree as described in
# http://ccr.sigcomm.org/online/files/p63-alfares.pdf
coreswitches = [FireSimSwitchNode() for x in range(4)]
self.roots = coreswitches
aggrswitches = [FireSimSwitchNode() for x in range(8)]
edgeswitches = [FireSimSwitchNode() for x in range(8)]
servers = [FireSimServerNode() for x in range(16)]
for switchno in range(len(coreswitches)):
core = coreswitches[switchno]
base = 0 if switchno < 2 else 1
dls = range(base, 8, 2)
dls = map(lambda x: aggrswitches[x], dls)
core.add_downlinks(dls)
for switchbaseno in range(0, len(aggrswitches), 2):
switchno = switchbaseno + 0
aggr = aggrswitches[switchno]
aggr.add_downlinks([edgeswitches[switchno], edgeswitches[switchno+1]])
switchno = switchbaseno + 1
aggr = aggrswitches[switchno]
aggr.add_downlinks([edgeswitches[switchno-1], edgeswitches[switchno]])
for edgeno in range(len(edgeswitches)):
edgeswitches[edgeno].add_downlinks([servers[edgeno*2], servers[edgeno*2+1]])
def custom_mapper(fsim_topol_with_passes):
""" In a custom mapper, you have access to the firesim topology with passes,
where you can access the run_farm nodes:
fsim_topol_with_passes.run_farm.{f1_16s, f1_2s, m4_16s}
To map, call add_switch or add_simulation on run farm instance
objs in the aforementioned arrays.
Because of the scope of this fn, you also have access to whatever
stuff you created in the topology itself, which we expect will be
useful for performing the mapping."""
# map the fat tree onto one m4.16xlarge (for core switches)
# and two f1.16xlarges (two pods of aggr/edge/4sims per f1.16xlarge)
for core in coreswitches:
fsim_topol_with_passes.run_farm.m4_16s[0].add_switch(core)
for aggrsw in aggrswitches[:4]:
fsim_topol_with_passes.run_farm.f1_16s[0].add_switch(aggrsw)
for aggrsw in aggrswitches[4:]:
fsim_topol_with_passes.run_farm.f1_16s[1].add_switch(aggrsw)
for edgesw in edgeswitches[:4]:
fsim_topol_with_passes.run_farm.f1_16s[0].add_switch(edgesw)
for edgesw in edgeswitches[4:]:
fsim_topol_with_passes.run_farm.f1_16s[1].add_switch(edgesw)
for sim in servers[:8]:
fsim_topol_with_passes.run_farm.f1_16s[0].add_simulation(sim)
for sim in servers[8:]:
fsim_topol_with_passes.run_farm.f1_16s[1].add_simulation(sim)
self.custom_mapper = custom_mapper
def example_multilink(self):
self.roots = [FireSimSwitchNode()]
midswitch = FireSimSwitchNode()
lowerlayer = [midswitch for x in range(16)]
self.roots[0].add_downlinks(lowerlayer)
servers = [FireSimServerNode()]
midswitch.add_downlinks(servers)
def example_multilink_32(self):
self.roots = [FireSimSwitchNode()]
midswitch = FireSimSwitchNode()
lowerlayer = [midswitch for x in range(32)]
self.roots[0].add_downlinks(lowerlayer)
servers = [FireSimServerNode()]
midswitch.add_downlinks(servers)
def example_multilink_64(self):
self.roots = [FireSimSwitchNode()]
midswitch = FireSimSwitchNode()
lowerlayer = [midswitch for x in range(64)]
self.roots[0].add_downlinks(lowerlayer)
servers = [FireSimServerNode()]
midswitch.add_downlinks(servers)
def example_cross_links(self):
self.roots = [FireSimSwitchNode() for x in range(2)]
midswitches = [FireSimSwitchNode() for x in range(2)]
self.roots[0].add_downlinks(midswitches)
self.roots[1].add_downlinks(midswitches)
servers = [FireSimServerNode() for x in range(2)]
midswitches[0].add_downlinks([servers[0]])
midswitches[1].add_downlinks([servers[1]])
def small_hierarchy_8sims(self):
self.custom_mapper = 'mapping_use_one_f1_16xlarge'
self.roots = [FireSimSwitchNode()]
midlevel = [FireSimSwitchNode() for x in range(4)]
servers = [[FireSimServerNode() for x in range(2)] for x in range(4)]
self.roots[0].add_downlinks(midlevel)
for swno in range(len(midlevel)):
midlevel[swno].add_downlinks(servers[swno])
def small_hierarchy_2sims(self):
self.custom_mapper = 'mapping_use_one_f1_16xlarge'
self.roots = [FireSimSwitchNode()]
midlevel = [FireSimSwitchNode() for x in range(1)]
servers = [[FireSimServerNode() for x in range(2)] for x in range(1)]
self.roots[0].add_downlinks(midlevel)
for swno in range(len(midlevel)):
midlevel[swno].add_downlinks(servers[swno])
def example_1config(self):
self.roots = [FireSimSwitchNode()]
servers = [FireSimServerNode() for y in range(1)]

View File

@ -41,7 +41,7 @@ static void simplify_frac(int n, int d, int *nn, int *dd)
simplenic_t::simplenic_t(
simif_t *sim, char *slotid,
uint64_t mac_little_end, int netbw, int netburst, int linklatency,
char *niclogfile, bool loopback): endpoint_t(sim)
char *niclogfile, bool loopback, char *shmemportname): endpoint_t(sim)
{
#ifdef SIMPLENICWIDGET_0
// store link latency:
@ -71,17 +71,31 @@ simplenic_t::simplenic_t(
}
}
char name[100];
char name[257];
int shmemfd;
if (!loopback) {
for (int j = 0; j < 2; j++) {
sprintf(name, "/port_nts%s_%d", slotid, j);
if (shmemportname) {
printf("Using non-slot-id associated shmemportname:\n");
sprintf(name, "/port_nts%s_%d", shmemportname, j);
} else {
printf("Using slot-id associated shmemportname:\n");
sprintf(name, "/port_nts%s_%d", slotid, j);
}
printf("opening/creating shmem region %s\n", name);
shmemfd = shm_open(name, O_RDWR | O_CREAT , S_IRWXU);
ftruncate(shmemfd, BUFBYTES+EXTRABYTES);
pcis_read_bufs[j] = (char*)mmap(NULL, BUFBYTES+EXTRABYTES, PROT_READ | PROT_WRITE, MAP_SHARED, shmemfd, 0);
sprintf(name, "/port_stn%s_%d", slotid, j);
if (shmemportname) {
printf("Using non-slot-id associated shmemportname:\n");
sprintf(name, "/port_stn%s_%d", shmemportname, j);
} else {
printf("Using slot-id associated shmemportname:\n");
sprintf(name, "/port_stn%s_%d", slotid, j);
}
printf("opening/creating shmem region %s\n", name);
shmemfd = shm_open(name, O_RDWR | O_CREAT , S_IRWXU);
ftruncate(shmemfd, BUFBYTES+EXTRABYTES);

View File

@ -15,7 +15,7 @@
class simplenic_t: public endpoint_t
{
public:
simplenic_t(simif_t* sim, char * slotid, uint64_t mac_little_end, int netbw, int netburst, int linklatency, char * niclogfile, bool loopback);
simplenic_t(simif_t* sim, char * slotid, uint64_t mac_little_end, int netbw, int netburst, int linklatency, char * niclogfile, bool loopback, char *shmemportname);
~simplenic_t();
virtual void init();

View File

@ -19,6 +19,7 @@ firesim_top_t::firesim_top_t(int argc, char** argv)
char * niclogfile = NULL;
char * slotid = NULL;
char * tracefile = NULL;
char * shmemportname = NULL;
uint64_t mac_little_end = 0; // default to invalid mac addr, force user to specify one
uint64_t trace_start = 0, trace_end = ULONG_MAX;
int netbw = MAX_BANDWIDTH, netburst = 8;
@ -42,6 +43,12 @@ firesim_top_t::firesim_top_t(int argc, char** argv)
if (arg.find("+slotid=") == 0) {
slotid = const_cast<char*>(arg.c_str()) + 8;
}
// TODO: move this and a bunch of other NIC arg parsing into the nic endpoint code itself
if (arg.find("+shmemportname=") == 0) {
shmemportname = const_cast<char*>(arg.c_str()) + 15;
}
if (arg.find("+zero-out-dram") == 0) {
do_zero_out_dram = true;
}
@ -111,7 +118,7 @@ firesim_top_t::firesim_top_t(int argc, char** argv)
#endif
add_endpoint(new blockdev_t(this, args));
add_endpoint(new simplenic_t(this, slotid, mac_little_end, netbw, netburst, linklatency, niclogfile, nic_loopback));
add_endpoint(new simplenic_t(this, slotid, mac_little_end, netbw, netburst, linklatency, niclogfile, nic_loopback, shmemportname));
add_endpoint(new tracerv_t(this, tracefile, trace_start, trace_end));
// add more endpoints here

@ -1 +1 @@
Subproject commit 6640fc7cc787938c0cb012673c4fcdb1f4c851ff
Subproject commit 8e475fa6597ac1383a4241db2badeed4826f234f

View File

@ -57,12 +57,14 @@ void BasePort::write_flits_to_output() {
uint64_t basetime = this_iter_cycles_start;
uint64_t maxtime = this_iter_cycles_start + LINKLATENCY;
bool empty_buf = true;
while (!(outputqueue.empty())) {
// first, check timing boundaries.
uint64_t space_available = LINKLATENCY - flitswritten;
uint64_t outputtimestamp = outputqueue.front()->timestamp;
uint64_t outputtimestampend = outputtimestamp + outputqueue.front()->amtwritten;
// confirm that a) we are allowed to send this out based on timestamp
// b) we are allowed to send this out based on available space (TODO fix)
if (outputtimestampend < maxtime && (outputqueue.front()->amtwritten <= space_available)) {
@ -91,6 +93,7 @@ void BasePort::write_flits_to_output() {
write_last_flit(current_output_buf, flitswritten, i == (thispacket->amtwritten-1));
write_valid_flit(current_output_buf, flitswritten);
write_flit(current_output_buf, flitswritten, thispacket->dat[i]);
empty_buf = false;
flitswritten++;
}
free(thispacket);
@ -100,6 +103,9 @@ void BasePort::write_flits_to_output() {
break;
}
}
if (empty_buf) {
((uint64_t*)current_output_buf)[0] = 0xDEADBEEFDEADBEEFL;
}
}
// initialize output port fullness for this round

View File

@ -1,3 +1,5 @@
#include <stdlib.h>
/* ----------------------------------------------------
* buffer flit operations
*
@ -63,6 +65,13 @@ uint16_t get_port_from_flit(uint64_t flit, int current_port) {
if (sendport != 0xffff) {
sendport = mac2port[sendport];
}
if (sendport == NUMDOWNLINKS) {
// this has been mapped to "any uplink", so pick one
int randval = rand() % NUMUPLINKS;
sendport = randval + NUMDOWNLINKS;
// printf("sending to random uplink.\n");
// printf("port: %04x\n", sendport);
}
//printf("port: %04x\n", sendport);
return sendport;
}

View File

@ -1,20 +0,0 @@
// THIS FILE IS MACHINE GENERATED. SEE emitconfig.py
#ifdef NUMCLIENTSCONFIG
#define NUMPORTS 8
#endif
#ifdef PORTSETUPCONFIG
ports[0] = new ShmemPort(0);
ports[1] = new ShmemPort(1);
ports[2] = new ShmemPort(2);
ports[3] = new ShmemPort(3);
ports[4] = new ShmemPort(4);
ports[5] = new ShmemPort(5);
ports[6] = new ShmemPort(6);
ports[7] = new ShmemPort(7);
#endif
#ifdef MACPORTSCONFIG
uint16_t mac2port[10] {8, 8, 0, 1, 2, 3, 4, 5, 6, 7};
#endif

View File

@ -1,8 +1,8 @@
#include <errno.h>
class ShmemPort : public BasePort {
public:
ShmemPort(int portNo);
ShmemPort(int portNo, char * shmemportname, bool uplink);
void tick();
void tick_pre();
void send();
@ -13,26 +13,116 @@ class ShmemPort : public BasePort {
int currentround = 0;
};
ShmemPort::ShmemPort(int portNo) : BasePort(portNo) {
ShmemPort::ShmemPort(int portNo, char * shmemportname, bool uplink) : BasePort(portNo) {
#define SHMEM_EXTRABYTES 1
// create shared memory regions
char name[100];
int shmemfd;
for (int j = 0; j < 2; j++) {
sprintf(name, "/port_nts%d_%d", _portNo, j);
printf("opening/creating shmem region %s\n", name);
shmemfd = shm_open(name, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
ftruncate(shmemfd, BUFSIZE_BYTES+SHMEM_EXTRABYTES);
recvbufs[j] = (uint8_t*)mmap(NULL, BUFSIZE_BYTES+SHMEM_EXTRABYTES, PROT_READ | PROT_WRITE, MAP_SHARED, shmemfd,0);
memset(recvbufs[j], 0, BUFSIZE_BYTES+SHMEM_EXTRABYTES);
sprintf(name, "/port_stn%d_%d", _portNo, j);
printf("opening/creating shmem region %s\n", name);
shmemfd = shm_open(name, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
ftruncate(shmemfd, BUFSIZE_BYTES+SHMEM_EXTRABYTES);
char * recvdirection;
char * senddirection;
int ftresult;
int shm_flags;
if (uplink) {
// uplink should not truncate on SHM_OPEN
shm_flags = O_RDWR /*| O_CREAT*/;
} else {
shm_flags = O_RDWR | O_CREAT | O_TRUNC;
}
if (uplink) {
fprintf(stdout, "Uplink Port\n");
recvdirection = "stn";
senddirection = "nts";
} else {
fprintf(stdout, "Downlink Port\n");
recvdirection = "nts";
senddirection = "stn";
}
for (int j = 0; j < 2; j++) {
if (shmemportname) {
fprintf(stdout, "Using non-slot-id associated shmemportname:\n");
sprintf(name, "/port_%s%s_%d", recvdirection, shmemportname, j);
} else {
fprintf(stdout, "Using slot-id associated shmemportname:\n");
sprintf(name, "/port_%s%d_%d", recvdirection, _portNo, j);
}
fprintf(stdout, "opening/creating shmem region\n%s\n", name);
shmemfd = shm_open(name, shm_flags, S_IRWXU);
while (shmemfd == -1) {
perror("shm_open failed");
if (uplink) {
fprintf(stdout, "retrying in 1s...\n");
sleep(1);
shmemfd = shm_open(name, shm_flags, S_IRWXU);
} else {
abort();
}
}
if (!uplink) {
ftresult = ftruncate(shmemfd, BUFSIZE_BYTES+SHMEM_EXTRABYTES);
if (ftresult == -1) {
perror("ftruncate failed");
abort();
}
}
recvbufs[j] = (uint8_t*)mmap(NULL, BUFSIZE_BYTES+SHMEM_EXTRABYTES, PROT_READ | PROT_WRITE, MAP_SHARED, shmemfd,0);
if (recvbufs[j] == MAP_FAILED) {
perror("mmap failed");
abort();
}
if (!uplink) {
memset(recvbufs[j], 0, BUFSIZE_BYTES+SHMEM_EXTRABYTES);
}
if (shmemportname) {
fprintf(stdout, "Using non-slot-id associated shmemportname:\n");
sprintf(name, "/port_%s%s_%d", senddirection, shmemportname, j);
} else {
fprintf(stdout, "Using slot-id associated shmemportname:\n");
sprintf(name, "/port_%s%d_%d", senddirection, _portNo, j);
}
fprintf(stdout, "opening/creating shmem region\n%s\n", name);
shmemfd = shm_open(name, shm_flags, S_IRWXU);
while (shmemfd == -1) {
perror("shm_open failed");
if (uplink) {
fprintf(stdout, "retrying in 1s...\n");
sleep(1);
shmemfd = shm_open(name, shm_flags, S_IRWXU);
} else {
abort();
}
}
if (!uplink) {
ftresult = ftruncate(shmemfd, BUFSIZE_BYTES+SHMEM_EXTRABYTES);
if (ftresult == -1) {
perror("ftruncate failed");
abort();
}
}
sendbufs[j] = (uint8_t*)mmap(NULL, BUFSIZE_BYTES+SHMEM_EXTRABYTES, PROT_READ | PROT_WRITE, MAP_SHARED, shmemfd,0);
memset(sendbufs[j], 0, BUFSIZE_BYTES+SHMEM_EXTRABYTES);
if (sendbufs[j] == MAP_FAILED) {
perror("mmap failed");
abort();
}
if (!uplink) {
memset(sendbufs[j], 0, BUFSIZE_BYTES+SHMEM_EXTRABYTES);
}
}
// setup "current" bufs. tick will swap for shmem passing
@ -41,6 +131,11 @@ ShmemPort::ShmemPort(int portNo) : BasePort(portNo) {
}
void ShmemPort::send() {
if (((uint64_t*)current_output_buf)[0] == 0xDEADBEEFDEADBEEFL) {
// if compress flag is set, clear it, this port type doesn't care
// (and in fact, we're writing too much, so stuff later will get confused)
((uint64_t*)current_output_buf)[0] = 0L;
}
// mark flag to initiate "send"
current_output_buf[BUFSIZE_BYTES] = 1;
}

View File

@ -47,12 +47,29 @@ SocketClientPort::SocketClientPort(int portNo, char * serverip, int hostport) :
}
void SocketClientPort::send() {
int amtsent = ::send(clientsocket, current_output_buf, BUFSIZE_BYTES, 0);
if (amtsent != BUFSIZE_BYTES) { printf("SOCKETPORT SEND ERROR\n"); exit(1); }
if (((uint64_t*)current_output_buf)[0] == 0xDEADBEEFDEADBEEFL) {
// printf("sending compressed\n");
#define COMPRESS_NUM_BYTES (8)
int amtsent = ::send(clientsocket, current_output_buf, COMPRESS_NUM_BYTES, 0);
if (amtsent != COMPRESS_NUM_BYTES) { printf("SOCKETPORT SEND ERROR\n"); exit(1); }
} else {
int amtsent = ::send(clientsocket, current_output_buf, BUFSIZE_BYTES, 0);
if (amtsent != BUFSIZE_BYTES) { printf("SOCKETPORT SEND ERROR\n"); exit(1); }
}
}
void SocketClientPort::recv() {
int amtread = 0;
while (amtread < COMPRESS_NUM_BYTES) {
amtread += ::recv(clientsocket, current_input_buf + amtread,
COMPRESS_NUM_BYTES - amtread, 0);
}
if (((uint64_t*)current_input_buf)[0] == 0xDEADBEEFDEADBEEFL) {
// printf("recv compressed\n");
memset(current_input_buf, 0x0, BUFSIZE_BYTES);
return;
}
while (amtread < BUFSIZE_BYTES) {
amtread += ::recv(clientsocket, current_input_buf + amtread,
BUFSIZE_BYTES - amtread, 0);
@ -125,12 +142,29 @@ SocketServerPort::SocketServerPort(int portNo, int hostport) : BasePort(portNo)
}
void SocketServerPort::send() {
int amtsent = ::send(serversocket, current_output_buf, BUFSIZE_BYTES, 0);
if (amtsent != BUFSIZE_BYTES) { printf("SOCKETPORT SEND ERROR\n"); exit(1); }
if (((uint64_t*)current_output_buf)[0] == 0xDEADBEEFDEADBEEFL) {
// printf("sending compressed\n");
#define COMPRESS_NUM_BYTES (8)
int amtsent = ::send(serversocket, current_output_buf, COMPRESS_NUM_BYTES, 0);
if (amtsent != COMPRESS_NUM_BYTES) { printf("SOCKETPORT SEND ERROR\n"); exit(1); }
} else {
int amtsent = ::send(serversocket, current_output_buf, BUFSIZE_BYTES, 0);
if (amtsent != BUFSIZE_BYTES) { printf("SOCKETPORT SEND ERROR\n"); exit(1); }
}
}
void SocketServerPort::recv() {
int amtread = 0;
while (amtread < COMPRESS_NUM_BYTES) {
amtread += ::recv(serversocket, current_input_buf + amtread,
COMPRESS_NUM_BYTES - amtread, 0);
}
if (((uint64_t*)current_input_buf)[0] == 0xDEADBEEFDEADBEEFL) {
// printf("recv compressed\n");
memset(current_input_buf, 0x0, BUFSIZE_BYTES);
return;
}
while (amtread < BUFSIZE_BYTES) {
amtread += ::recv(serversocket, current_input_buf + amtread,
BUFSIZE_BYTES - amtread, 0);

View File

@ -87,6 +87,12 @@ void SSHPort::send() {
// (data is in current_output_buf)
// and push it into queues to send into the TAP
if (((uint64_t*)current_output_buf)[0] == 0xDEADBEEFDEADBEEFL) {
// if compress flag is set, clear it, this port type doesn't care
// (and in fact, we're writing too much, so stuff later will get confused)
((uint64_t*)current_output_buf)[0] = 0L;
}
// first, push into out_flits queue
for (int tokenno = 0; tokenno < NUM_TOKENS; tokenno++) {
if (is_valid_flit(current_output_buf, tokenno)) {

View File

@ -159,7 +159,11 @@ while (!pqueue.empty()) {
printf("packet for port: %x\n", send_to_port);
printf("packet timestamp: %ld\n", tsp->timestamp);
if (send_to_port == BROADCAST_ADJUSTED) {
for (int i = 0; i < NUMPORTS; i++) {
#define ADDUPLINK (NUMUPLINKS > 0 ? 1 : 0)
// this will only send broadcasts to the first (zeroeth) uplink.
// on a switch receiving broadcast packet from an uplink, this should
// automatically prevent switch from sending the broadcast to any uplink
for (int i = 0; i < NUMDOWNLINKS + ADDUPLINK; i++) {
if (i != tsp->sender ) {
switchpacket * tsp2 = (switchpacket*)malloc(sizeof(switchpacket));
memcpy(tsp2, tsp, sizeof(switchpacket));