SmallNetwork import no.uio.ifi.gruns.*; import no.uio.ifi.gruns.infiniband.*; import import import import java.text.DateFormat; java.util.Date; java.util.HashMap; java.util.Iterator; import java.io.*; /** * A small IBA-network to thoroughly test the no.uio.ifi.gruns.infiniband * classes. Sends only unidirectionally, the network is only connected from * left to right and top to down, not the other way around. */ public class SmallNetwork { public static final int STOP_TIME = 500000; public static final String LOG_FILE = "/tmp/erikbra_no.uio.ifi.gruns_run.log"; public static boolean verbose = false; public static final Kernel kernel = new Kernel(STOP_TIME); // The switches in our network public static IBASwitch[] ibasw; public static java.text.DateFormat df = DateFormat.getTimeInstance(DateFormat.LONG); public static long realStartTime, realStopTime; public static void preInfo(){ realStartTime = System.currentTimeMillis(); Reporter.println("Started simulation of "+Utils.formatNumber(STOP_TIME)+ " time units. Time is "+ df.format(new Date(realStartTime))); Reporter.println("Press Crtl-C to stop simulation on the "+ "current time step."); } public static void postInfo(){ realStopTime = System.currentTimeMillis(); Reporter.getOutputStream().flush(); Reporter.println(); Reporter.println("Finished simulation of "+ kernel.currentTime()+" time units. df.format(new Date(realStopTime))); Time is "+ long timeUsed = realStopTime - realStartTime; Reporter.println("Total Reporter.println(); time used: "+Utils.timeFormat(timeUsed)); // Avoid returning an error value because Crtl-C is pressed. // It’s perfectly legal to stop the simulation before it’s finished. Runtime.getRuntime().halt(0); } public static void main(String [] args){ FileOutputStream outFile = null; long realStartTime, realStopTime; Reporter.setOutputStream(System.out); /* try{ outFile = new FileOutputStream(LOG_FILE); Reporter.setOutputStream(new PrintStream(outFile)); 1 }catch(FileNotFoundException fnfe){ System.err.println("Error creating log file "+LOG_FILE); } */ // Print out some info before we start preInfo(); // Make sure there’s _some_ sensible output // if the simulation is aborted. Runtime.getRuntime().addShutdownHook(new Thread(){ public void run(){ SmallNetwork.postInfo(); }}); try{ IBAConnectionTools.setKernel(kernel); // buildNetwork(); gridNetwork(); }catch(RuntimeException e){ Reporter.println("RuntimeException occured!"); // Print out some info about the unit that crashed if (kernel.currentUnit() != null){ Reporter.println("kernel.currentUnit = "+ ((Reporting)kernel.currentUnit()).report()); } e.printStackTrace(Reporter.getOutputStream()); } // Print out some summarizing info postInfo(); // Close the output file if (outFile != null){ try{ outFile.close(); }catch(IOException ioe){ System.err.println("Error closing log file: "+LOG_FILE); } } } public static void gridNetwork(){ int height = 4; int width = 4; int numVLs = 2; int inputBufferSize = 4 * Integer.parseInt(System.getProperty("infiniband.EXP_PACKET_SIZE")); int outputBufferSize = 4 * Integer.parseInt(System.getProperty("infiniband.EXP_PACKET_SIZE")); IBASourceNode[] ibas = new IBASourceNode[height]; IBADrainNode ibad; // DEBUG long freeMem; Reporter.println(); System.gc(); freeMem = Runtime.getRuntime().freeMemory(); Reporter.debugln("Free Memory before allocation: \t"+ Utils.formatNumber(Runtime.getRuntime().freeMemory()/(1024))+" // // DO NOT REMOVE!! ibasw = IBAConnectionTools.simplexGrid(height, width, numVLs, inputBufferSize, outputBufferSize); // // DEBUG Reporter.debugln("Size of grid ("+height+"x"+width+"): \t\t"+ 2 kB"); Utils.formatNumber((freeMem-Runtime.getRuntime().freeMemory())/(1024))+" kB"); System.gc(); freeMem = Runtime.getRuntime().freeMemory(); "jalla", kernel); of switch: \t\t\t"+ //Utils.formatNumber((freeMem-Runtime.getRuntime().freeMemory()))+" B"); Utils.formatNumber(((freeMem-Runtime.getRuntime().freeMemory())/1024))+" kB"); IBASwitch test = new IBASwitch(3, 3, numVLs, inputBufferSize, outputBufferSize, Reporter.debugln("Size System.gc(); freeMem = Runtime.getRuntime().freeMemory(); Link link = new Link(kernel); Reporter.debugln("Size of link: \t\t\t"+ //Utils.formatNumber((freeMem-Runtime.getRuntime().freeMemory()))+" B"); Utils.formatNumber(((freeMem-Runtime.getRuntime().freeMemory())/1024))+" kB"); System.gc(); freeMem = Runtime.getRuntime().freeMemory(); "Test source", kernel); of source node: \t\t"+ //Utils.formatNumber((freeMem-Runtime.getRuntime().freeMemory()))+" B"); Utils.formatNumber(((freeMem-Runtime.getRuntime().freeMemory())/1024))+" kB"); IBASourceNode s = new IBASourceNode(1, numVLs, Reporter.debugln("Size System.gc(); freeMem = Runtime.getRuntime().freeMemory(); "Test drain", kernel); of drain node: \t\t"+ //Utils.formatNumber((freeMem-Runtime.getRuntime().freeMemory()))+" B"); Utils.formatNumber(((freeMem-Runtime.getRuntime().freeMemory())/1024))+" kB"); IBADrainNode d = new IBADrainNode(1, numVLs, Reporter.debugln("Size System.gc(); freeMem = Runtime.getRuntime().freeMemory(); IBAPacket p = new IBAPacket(); p.lrh.setLNH(IBAPacket.LNH_IBA_local); p.bth.setOpCode(IBAPacket.RD_SEND_ONLY); StringBuffer str = new StringBuffer(); int numChars = Integer.parseInt(System.getProperty("infiniband.EXP_PAYLOAD_SIZE")); for (int i=0; i < numChars; i++){ str.append((char) ((int) ’A’)); } p.pyld.setContent(str.toString().getBytes()); p.updatePacketLength(); Reporter.debugln("Size of IBA packet: \t\t"+ Utils.formatNumber((freeMem-Runtime.getRuntime().freeMemory()))+" B"); //Utils.formatNumber(((freeMem-Runtime.getRuntime().freeMemory())/1024))+" kB"); System.gc(); Reporter.debugln("Free Memory after allocation: \t"+ Utils.formatNumber(Runtime.getRuntime().freeMemory()/(1024))+" Reporter.println(); // kB"); System.exit(0); // // Connect source nodes for (int i=0; i < height; i++){ ibas[i] = new IBASourceNode(1, numVLs, "Source "+(i+1), kernel); ibas[i].setOwnAddress("1."+(i+1)); IBAConnectionTools.connect(ibas[i], 0, ibasw[i*width], 1); } // Drain node ibad = new IBADrainNode(1, numVLs, "Drain 1", kernel); IBAConnectionTools.connect(ibasw[7], 1, ibad, 0); // Configure each path int blocksPerPacket = (int) Math.ceil(((double)Integer.parseInt(System.getProperty("infiniband.EXP_PACKET_SIZE")))/ Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE"))); // Path 1 (from Source 1 to Drain 1) 3 ibas[0].addDestinationAddresses("2.1"); ibas[0].addRoutingInterval("2.1", 0); ibas[0].addHighPriority(0,0,1); // For debugging ibas[0].addHighPriority(0,1,1); ibasw[0].addRoutingInterval("2.1",1); ibasw[1].addRoutingInterval("2.1",1); ibasw[2].addRoutingInterval("2.1",2); ibasw[6].addRoutingInterval("2.1",1); ibasw[7].addRoutingInterval("2.1",1); // Path 2 (from Source 3 to Drain 1) ibas[2].addDestinationAddresses("2.1"); ibas[2].addRoutingInterval("2.1", 0); ibas[2].addHighPriority(0,1,1); // For debugging ibas[2].addHighPriority(0,0,1); ibasw[8].addRoutingInterval("2.1",1); ibasw[9].addRoutingInterval("2.1",1); ibasw[10].addRoutingInterval("2.1",0); // Priorities /* ibasw[8].addHighPriority(1,1,1); ibasw[9].addHighPriority(1,1,1); ibasw[10].addHighPriority(0,1,1); ibasw[6].addLowPriority(1,1,1); ibasw[7].addLowPriority(1,1,1); */ /* ibasw[0].addHighPriority(1,0,1); ibasw[1].addHighPriority(1,0,1); ibasw[2].addHighPriority(2,0,1); ibasw[6].addHighPriority(1,0,1); ibasw[7].addHighPriority(1,0,1); */ for (int i=0; i < ibasw.length; i++){ // Configure weights on output ports on the switches. for (int j=0; j < 3; j++){ for (int k=0; k < numVLs/2; k++){ ibasw[i].addLowPriority(j, k, 1); } for (int k=numVLs/2; k < numVLs; k++){ ibasw[i].addHighPriority(j, k, 1); } // limit of high priority //ibasw[i].setLimitOfHighPriority(j, 1 * blocksPerPacket); } } // // ibasw[6].setLimitOfHighPriority(1, 10 * blocksPerPacket); ibasw[7].setLimitOfHighPriority(1, 10 * blocksPerPacket); ibasw[6].setLimitOfHighPriority(1, 255); ibasw[7].setLimitOfHighPriority(1, 255); // Set service level ibas[0].setSL(0); 4 ibas[2].setSL(1); // Schedule sources kernel.schedule(ibas[0], 0); kernel.schedule(ibas[2], 0); // Start simulation kernel.simulate(); // Print out some stats Reporter.println(); Reporter.println("Simulation statistics:"); Reporter.println("=================================================="); // Print out report for all source nodes for (int i=0; i < height; i++){ Reporter.println(ibas[i].report()); } // Print out report for drain node Reporter.println(ibad.report()); // Print out info about dropped packets Reporter.println(); HashMap dropped = IBAStatistics.getDroppedPacketStats(); int totalDropped = 0; if (dropped.keySet().size() > 0){ Iterator it = dropped.keySet().iterator(); while (it.hasNext()){ IBASourceNode node = (IBASourceNode) it.next(); totalDropped += ((Integer) dropped.get(node)).intValue(); } } Reporter.println("Total number totalDropped); of dropped packets = "+ // DEBUG /* Reporter.println("Switch 7:"); Reporter.println("Total number of packets scheduled:"); Reporter.println("VL 0: "+ibasw[6].outputPorts[1].getTotalNumberOfPacketsScheduled(0)); Reporter.println("VL 1: "+ibasw[6].outputPorts[1].getTotalNumberOfPacketsScheduled(1)); Reporter.println("Total number of packets sent:"); Reporter.println("VL 0: "+ibasw[6].outputPorts[1].getTotalNumberOfPacketsSent(0)); Reporter.println("VL 1: "+ibasw[6].outputPorts[1].getTotalNumberOfPacketsSent(1)); Reporter.println("Switch 8:"); Reporter.println("Total number of packets scheduled:"); Reporter.println("VL 0: "+ibasw[7].outputPorts[1].getTotalNumberOfPacketsScheduled(0)); Reporter.println("VL 1: "+ibasw[7].outputPorts[1].getTotalNumberOfPacketsScheduled(1)); Reporter.println("Total number of packets sent:"); Reporter.println("VL 0: "+ibasw[7].outputPorts[1].getTotalNumberOfPacketsSent(0)); Reporter.println("VL 1: "+ibasw[7].outputPorts[1].getTotalNumberOfPacketsSent(1)); */ } public static void buildNetwork(){ int i; int int int int int numSourceNodes = 2; numDrainNodes[] = {2,1}; numVirtualLanes = 1; numIngressSwitches = 2; numSwitches = numIngressSwitches + 2; IBADrainNode[] ibad1 = new IBADrainNode[numDrainNodes[0]]; IBADrainNode[] ibad2 = new IBADrainNode[numDrainNodes[1]]; IBASourceNode[][] ibas = new IBASourceNode[2][numSourceNodes]; ibasw = new IBASwitch[numSwitches]; 5 Link tmpLink; IBAConnectionTools.setKernel(kernel); // First and second Switch for (int j=0; j < numIngressSwitches; j++){ ibasw[j] = new IBASwitch(numSourceNodes, 2, numVirtualLanes, kernel); "Sw"+(j+1), ibasw[j].addRoutingInterval("126.100-126.101",0); ibasw[j].addRoutingInterval("126.102",1); ibasw[j].addHighPriority(0, 0, 1); ibasw[j].addHighPriority(1, 0, 1); for (i=0; i < numSourceNodes; i++){ ibas[j][i] = new IBASourceNode(1,1,"I"+(j+1)+","+ "S"+(i+1), kernel); ibas[j][i].setOwnAddress("127."+(j*10)+i); ibas[j][i].addDestinationAddresses("126.100-126.102"); ibas[j][i].addRoutingInterval("126.100-126.102", 0); ibas[j][i].addHighPriority(0, 0, 1); IBAConnectionTools.connect(ibas[j][i], 0, ibasw[j], i); kernel.schedule(ibas[j][i], 0); } } // Third Switch ibasw[2] = new IBASwitch(2, 2, numVirtualLanes, kernel); "Sw3", ibasw[2].addRoutingInterval("126.100",0); ibasw[2].addHighPriority(0, 0, 1); ibasw[2].addRoutingInterval("126.101",1); ibasw[2].addHighPriority(1, 0, 1); // Fourth Switch ibasw[3] = new IBASwitch(2, 2, numVirtualLanes, kernel); "Sw4", ibasw[3].addRoutingInterval("126.102",0); ibasw[3].addHighPriority(0, 0, 1); // Connect switches IBAConnectionTools.connect(ibasw[0], IBAConnectionTools.connect(ibasw[0], IBAConnectionTools.connect(ibasw[1], IBAConnectionTools.connect(ibasw[1], 0, 1, 0, 1, ibasw[2], ibasw[3], ibasw[2], ibasw[3], 0); 0); 1); 1); // Drain nodes for (i=0; i < numDrainNodes[0]; i++){ ibad1[i] = new IBADrainNode(1, 1, "E1,D"+(i+1), kernel); IBAConnectionTools.connect(ibasw[2], i, ibad1[i], 0); } for (i=0; i < numDrainNodes[1]; i++){ ibad2[i] = new IBADrainNode(1, 1, "E2,D"+(i+1), kernel); IBAConnectionTools.connect(ibasw[3], i, ibad2[i], 0); } // Start simulation kernel.simulate(); // Print out some stats 6 Reporter.println(); Reporter.println("Simulation statistics:"); Reporter.println("=================================================="); // Print out report for all source nodes for (int j=0; j < numIngressSwitches; j++){ for (i=0; i < numSourceNodes; i++){ Reporter.println(ibas[j][i].report()); } } // Print out report for all drain nodes for (i=0; i < numDrainNodes[0]; i++){ Reporter.println(ibad1[i].report()); } for (i=0; i < numDrainNodes[1]; i++){ Reporter.println(ibad2[i].report()); } // Print out info about dropped packets Reporter.println(); Reporter.println("Dropped packets: "); HashMap dropped = IBAStatistics.getDroppedPacketStats(); if (dropped.keySet().size() > 0){ Iterator it = dropped.keySet().iterator(); while (it.hasNext()){ String id = (String) it.next(); Reporter.println("Node \""+id+"\": number of dropped ((Integer) dropped.get(id)).toString()); } } else{ Reporter.println("No dropped packets. Nice work! ;)"); } } } 7 packets: "+ IBAUnitComparatorTest import no.uio.ifi.gruns.*; import no.uio.ifi.gruns.infiniband.*; public class IBAUnitComparatorTest { public static void main(String [] args) { String[] sources = new String[150]; Kernel kernel = new Kernel(0); IBAUnitComparator comp = new IBAUnitComparator(); for (int i=0; i < sources.length; i++) { sources[i] = "1."+i; } //comp.compare(sources[0], sources[1]); for (int i=0; i < sources.length; i++) { int compVal = comp.compare(sources[75], sources[i]); System.err.println("sources[75] is "+ (compVal < 0 ? "smaller than": compVal == 0 ? "equal to": "bigger than")+ " sources["+i+"]."); } } } 8 GrunsTest import no.uio.ifi.gruns.*; import no.uio.ifi.gruns.infiniband.*; import import import import java.text.DateFormat; java.util.Date; java.util.HashMap; java.util.Iterator; import java.io.*; /** * This class is just made for testing the classes * in the no.uio.ifi.gruns package separately. */ public class GrunsTest { public static final int STOP_TIME = 5000000; public static final String LOG_FILE = "/tmp/erikbra_no.uio.ifi.gruns_run.log"; public static boolean verbose = false; public static final Kernel kernel = new Kernel(STOP_TIME); public static java.text.DateFormat df = DateFormat.getTimeInstance(DateFormat.LONG); public static long realStartTime, realStopTime; public static IBASourceNode[] ibas; public static IBASwitch[] ibasw; public static IBADrainNode[] ibad; public static void preInfo(){ realStartTime = System.currentTimeMillis(); Reporter.println("Started simulation of "+STOP_TIME+" df.format(new Date(realStartTime))); Reporter.println("Press time units. Time is "+ Crtl-C to stop simulation on the current time step."); } public static void postInfo(){ realStopTime = System.currentTimeMillis(); Reporter.getOutputStream().flush(); // Print out some stats Reporter.println(); Reporter.println("Simulation statistics:"); Reporter.println("=================================================="); // Print out report for all source nodes for (int i=0; i < ibas.length; i++){ Reporter.println(ibas[i].report()); } // Print out report for drain nodes for (int i=0; i < ibad.length; i++){ Reporter.println(ibad[i].report()); } // Print out info about dropped packets Reporter.println(); HashMap dropped = IBAStatistics.getDroppedPacketStats(); int totalDropped = 0; if (dropped.keySet().size() > 0){ Iterator it = dropped.keySet().iterator(); while (it.hasNext()){ IBASourceNode node = (IBASourceNode) it.next(); totalDropped += ((Integer) dropped.get(node)).intValue(); } } 9 Reporter.println("Total number of dropped Utils.formatInt(totalDropped)); packets = "+ // Simulation summary Reporter.println(); Reporter.println("Finished simulation of "+ Utils.formatLong(kernel.currentTime())+ " time units. Time is "+ df.format(new Date(realStopTime))); long timeUsed = realStopTime - realStartTime; Reporter.println("Total Reporter.println(); time used: "+Utils.timeFormat(timeUsed)); } public static void main(String [] args){ FileOutputStream outFile = null; long realStartTime, realStopTime; Reporter.setOutputStream(System.out); Reporter.setKernel(kernel); // Initialize system properties IBAConstants.loadProperties(); /* try{ outFile = new FileOutputStream(LOG_FILE); Reporter.setOutputStream(new PrintStream(outFile)); }catch(FileNotFoundException fnfe){ System.err.println("Error creating log file "+LOG_FILE); } */ // Print out some info before we start preInfo(); // Make sure there’s _some_ sensible output // if the simulation is aborted. Runtime.getRuntime().addShutdownHook(new Thread(){ public void run(){ GrunsTest.postInfo(); }}); try{ // // // testPackets(); testRandomAddress(); testKernel(); testIBAPackets(); // testLink(); // testIBAOutputPort(); // testIBASourceNode(); //testIBASwitch(); }catch(RuntimeException e){ Reporter.println("RuntimeException occured!"); // Print out some info about the unit that crashed if (kernel.currentUnit() != null){ Reporter.println("kernel.currentUnit = "+((Reporting)kernel.currentUnit()).report()); } e.printStackTrace(Reporter.getOutputStream()); } // Print out some summarizing info postInfo(); // Close the output file 10 if (outFile != null){ try{ outFile.close(); }catch(IOException ioe){ System.err.println("Error closing log file: "+LOG_FILE); } } } /** Testing packets and addresses */ public static void testPackets(){ Reporter.println("IBAPacket.RD_SEND_ONLY = "+Utils.asBitString(IBAPacket.RD_SEND_ONLY)); Reporter.println("IBAPacket.RD_TYPE = "+Utils.asBitString(IBAPacket.RD_TYPE)); new IBAPacket().bth.setOpCode(IBAPacket.RD_SEND_ONLY); Reporter.println("\nTesting packets and addresses"); "192.168"; "Hello World"; String addr = String msg = Address a = new Address(addr); Packet p = new Packet(a, msg.getBytes()); Reporter.println("Sent packet to\t\t"+addr+" containing \""+msg+"\""); StringBuffer sb = new StringBuffer(); byte [] address = { p.getNextByte(), p.getNextByte() }; sb.append(new Address(address).getAddressAsString()); sb.append(" "); byte [] payload = new byte[p.getSize() - 2]; for (int i = 0; i < p.getSize() - 2; i++){ payload[i] = p.getNextByte(); } sb.append(new String(payload)); Reporter.println("Received packet to\t"+ new Address(address).getAddressAsString()+ " containing \""+ new String(payload)+"\""); } /** Testing IBA Packets */ public static void testIBAPackets(){ Address localAddress = new Address("127.100"); Address destination = new Address("127.101"); IBAPacket p = new IBAPacket(destination, localAddress); // Tell that this packet is a local packet p.lrh.setLNH(IBAPacket.LNH_IBA_local); // Make this a "Send Only" packet. p.bth.setOpCode(IBAPacket.RD_SEND_ONLY); p.lrh.setVL(5); p.lrh.setSL(14); StringBuffer str = new StringBuffer(); // Fill up with a random number of random characters // int numChars = random.nextInt(2000); // Use fixed number instead, for predicatability int numChars = Integer.parseInt(System.getProperty("infiniband.EXP_PAYLOAD_SIZE")); for (int i=0; i < numChars; i++){ // Should be a random character between A and Z //str.append((char) ((int) ’A’ + random.nextInt(26))); 11 str.append(’A’); } p.pyld.setContent(str.toString().getBytes()); p.updatePacketLength(); p.lrh.setVL(5); p.lrh.setLVer(13); p.lrh.setSL(14); Reporter.println("p.lrh.VL = "+p.lrh.VL()); = "+p.lrh.LVer()); Reporter.println("p.lrh.SL = "+p.lrh.SL()); Reporter.println("IBAPacketParser.packetLength(p.lrh) = "+IBAPacketParser.packetLength(p.lrh)); Reporter.println("p.lrh.PktLen() = "+p.lrh.PktLen()); Reporter.println("p.getSize() = "+p.getSize()); Reporter.println("p.getContent().length = "+p.getContent().length); Reporter.println("p.lrh.LVer } /** Testing link */ public static void testLink(){ Link l = new Link(kernel); byte[] source = {1,2,3,4,5,6,7,8}; byte[] copy = new byte[source.length]; for (int i=0; i < source.length; i++){ l.sendByte(source[i]); } l.act(); for (int i=0; i < copy.length; i++){ copy[i] = l.receiveByte(); } Reporter.print("source for = {"); (int i=0; i < source.length-1; i++){ Reporter.print(source[i]+","); } Reporter.print(source[source.length-1]+"}"); Reporter.println(); Reporter.print("copy for = {"); (int i=0; i < copy.length-1; i++){ Reporter.print(copy[i]+","); } Reporter.print(copy[copy.length-1]+"}"); Reporter.println(); } public static void testIBASourceNode(){ if (verbose){ Reporter.println("\nTesting IBA source node"); } int numLinks = 8; ibad = new IBADrainNode[1]; ibad[0] = new IBADrainNode(numLinks, 1, "D1", kernel); ibas = new IBASourceNode[numLinks]; Link[] links = new Link[numLinks]; for (int i=0; i < numLinks; i++){ ibas[i] = new IBASourceNode(1,1,"S"+i, kernel); ibas[i].setOwnAddress("127."+(50+i)); ibas[i].addDestinationAddresses("126.100-126.110"); ibas[i].addRoutingInterval("126.100-126.110", 0); ibas[i].addHighPriority(0, 0, 1); links[i] = new Link(kernel); ibas[i].attach(links[i], 0); ibad[0].attach(links[i], i); kernel.schedule(ibas[i], 0); 12 } kernel.simulate(); // Print out some stats Reporter.println(); Reporter.println("Simulation statistics:"); Reporter.println("=================================================="); // Print out report for all source nodes for (int i=0; i < ibas.length; i++){ Reporter.println(ibas[i].report()); } // Print out report for drain node Reporter.println(ibad[0].report()); } public static void testIBASwitch(){ if (verbose){ Reporter.println("\nTesting IBA switch"); } IBAConnectionTools.setKernel(kernel); int numSourceNodes = 2; int numDrainNodes = 1; int inputBufferSize = 4 * Integer.parseInt(System.getProperty("infiniband.EXP_PACKET_SIZE")); int outputBufferSize = 4 * Integer.parseInt(System.getProperty("infiniband.EXP_PACKET_SIZE")); int numVirtualLanes = 2; int numSwitches = 2; ibad = new IBADrainNode[numDrainNodes]; ibas = new IBASourceNode[numSourceNodes]; ibasw = new IBASwitch[numSwitches]; for (int i=0; i < numSwitches; i++){ ibasw[i] = new IBASwitch(numSourceNodes, numDrainNodes, numVirtualLanes, inputBufferSize, outputBufferSize, "TestSwitch "+i, kernel); } int bytesPerPacket = Integer.parseInt(System.getProperty("infiniband.EXP_PACKET_SIZE")); // int limitOfHighPriority = (int) Math.ceil( ((double) 1 * bytesPerPacket) / 4096); int limitOfHighPriority = 0; // DEBUG Reporter.debugln("bytes per packet = "+bytesPerPacket); of high pri = "+limitOfHighPriority); Reporter.debugln("Switch processing time = "+ System.getProperty("infiniband.PACKET_PROCESSING_TIME")); // Reporter.debugln("limit ibasw[0].setLimitOfHighPriority(0, limitOfHighPriority); ibasw[1].setLimitOfHighPriority(0, limitOfHighPriority); // Source nodes for (int i=0; i < numSourceNodes; i++){ ibas[i] = new IBASourceNode(1, numVirtualLanes,"Source "+i, kernel); ibas[i].setOwnAddress("127."+i); ibas[i].addDestinationAddresses("126.100-126.100"); ibas[i].addRoutingInterval("126.100-126.100", 0); IBAConnectionTools.connect(ibas[i], 0, ibasw[0], i); // Configure weights on output ports on the source nodes for (int j=0; j < numVirtualLanes/2; j++){ ibas[i].addLowPriority(0, j, 1); } 13 for (int j=numVirtualLanes/2; j < numVirtualLanes; j++){ ibas[i].addHighPriority(0, j, 1); } kernel.schedule(ibas[i], 0); } ibas[0].setSL(0); ibas[1].setSL(1); // // ibas[2].setSL(8); ibas[3].setSL(9); // Configure weights on output ports on switch 1 for (int j=0; j < numVirtualLanes/2; j++){ ibasw[0].addLowPriority(0, j, 1); } for (int j=numVirtualLanes/2; j < numVirtualLanes; j++){ ibasw[0].addHighPriority(0, j, 1); } // Connect switches IBAConnectionTools.connect(ibasw[0], 0, ibasw[1], 0); // Drain nodes and switch 2 for (int i=0; i < numDrainNodes; i++){ ibad[i] = new IBADrainNode(1, numVirtualLanes, "Drain IBAConnectionTools.connect(ibasw[1], 0, ibad[i], i); "+i, kernel); for (int j=0; j < numVirtualLanes/2; j++){ ibasw[1].addLowPriority(i, j, 1); } for (int j=numVirtualLanes/2; j < numVirtualLanes; j++){ ibasw[1].addHighPriority(i, j, 1); } } // Configure switch with routing tables // All to one for (int i=0; i < numSwitches; i++){ ibasw[i].addRoutingInterval("126.100-126.127", 0); } // Start simulation kernel.simulate(); // Print out some stats Reporter.println(); Reporter.println("Simulation statistics:"); Reporter.println("=================================================="); // Print out report for all source nodes for (int i=0; i < numSourceNodes; i++){ Reporter.println(ibas[i].report()); } // Print out report for all drain nodes for (int i=0; i < numDrainNodes; i++){ Reporter.println(ibad[i].report()); } } /** Testing random address generator */ public static void testRandomAddress(){ int NUM_ITERATIONS=10000; Address [] addresses = { new Address("0.0"), new Address("0.1"), new Address("120.138"), new Address("255.255"), new Address("192.168"), new Address("129.240") 14 }; int [] results = new int[addresses.length]; Reporter.println("\nTesting Random NUM_ITERATIONS+ Address generator on "+ " iterations."); Address a; for (int i = 0; i < NUM_ITERATIONS; i++){ a = RandomAddress.nextAddress(addresses); for (int j = 0; j < addresses.length; j++){ if (a.getAddress() == addresses[j].getAddress()){ results[j]++; } } // Reporter.println("Iteration "+i+", address "+a.getAddressAsString()); } Reporter.println("Address\t\tNumber for of times returned"); (int i=0; i < addresses.length; i++){ Reporter.println(addresses[i]+"\t\t"+results[i]); } } public static void testKernel(){ /* final int TIME = 1000 ; Scheduleable[] tmpUnit = new Scheduleable[3]; Reporter.println("\nTesting Kernel scheduling with "+ TIME + " time units."); Kernel k = new Kernel(TIME); for (int i=0; i < 3; i++){ tmpUnit[i] = new Unit(kernel){ public void act(){ Reporter.println("Scheduled for running on time "+ nextTimeScheduled); }}; } for (int i=0; i < TIME; i+=3){ k.schedule(tmpUnit[0], i); } for (int i=1; i < TIME; i+=2){ k.schedule(tmpUnit[1], i); } for (int i=78; i < TIME; i+=1){ k.schedule(tmpUnit[2], i); } 15 k.simulate(); */ } public static void testIBAOutputPort(){ int int int int N=1010; numVLs = 10; pyldSize = 500; numHighPriPackets = 2; int[] droppedPackets = new int[numVLs]; float pktSizeInUnits = ((float)(pyldSize + 38)) / Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE")); IBAOutputPort outputPort; IBADrainNode drainNode; IBAPacket packet; java.util.Random random = new java.util.Random(); outputPort = new IBAOutputPort(numVLs, kernel); outputPort.setID("TestOutputPort"); int n; for (n = 0; n < 1; n++){ outputPort.addHighPriority(n,1); } for (int m = n; m < numVLs; m++){ outputPort.addLowPriority(m,1); } //int limitOfHighPriority = (int)Math.ceil(pktSizeInUnits)*numHighPriPackets; int limitOfHighPriority = 255; // Reporter.debugln("limitOfHighPriority = "+limitOfHighPriority); outputPort.setLimitOfHighPriority(limitOfHighPriority); drainNode = new IBADrainNode(1, numVLs, "TestDrain", kernel); // connect the output port and the drain node Link l = new Link(kernel); drainNode.attach(l, 0); outputPort.attach(l); Address destAddr = new Address("127.110"); Address sourceAddr; for (int i=0; i < for (int j=0; N; i++){ j < numVLs; j++){ sourceAddr = new Address("127."+(100+j)); // Make a packet to the specified address packet = new IBAPacket(destAddr,sourceAddr); // Tell that this packet is a local packet packet.lrh.setLNH(IBAPacket.LNH_IBA_local); // Make this a "Send Only" packet. packet.bth.setOpCode(IBAPacket.RD_SEND_ONLY); packet.lrh.setVL(j); packet.lrh.setSL(j); StringBuffer str = new StringBuffer(); // Fill up with a random number of random characters // int numChars = random.nextInt(2000); // Use fixed number instead, to find errors int numChars = pyldSize; for (int k=0; k < numChars; k++){ // Should be a random character between A and Z str.append((char) ((int) ’A’ + random.nextInt(26))); } packet.pyld.setContent(str.toString().getBytes()); packet.updatePacketLength(); if (outputPort.canSend(j, IBAPacketParser.packetLength(packet.lrh))){ outputPort.sendIBAPacket(packet, j); }else{ droppedPackets[j]++; 16 } } kernel.schedule(outputPort, i); } kernel.simulate(); // Print out status Reporter.println(drainNode.report()); Reporter.println("Dropped for stats:"); (int i=0; i < droppedPackets.length; i++){ Reporter.println("VL "+i+": "+droppedPackets[i]); } } } 17 Main // {{{ import no.uio.ifi.gruns.*; import no.uio.ifi.gruns.infiniband.*; import import import import java.text.DateFormat; java.util.Date; java.util.HashMap; java.util.Iterator; import java.io.*; import javax.swing.*; import no.uio.ifi.gruns.gui.*; // For displaying host name on the gui title line. import java.net.InetAddress; /** * The main program that connects all the classes in the * no.uio.ifi.gruns packages to form a network simulator. */ public class Main { // {{{ Public static variables public static final int STOP_TIME = 200000; public static boolean verbose = false; public static Kernel kernel; /* The core switches in our network */ public static IBASwitch[] coreIbaSw; /* The edge switches in our network */ public static IBASwitch[] edgeIbaSw; /* The source nodes in our network */ public static IBASourceNode[] ibas; /* The drain nodes in our network */ public static IBADrainNode[] ibad; // Used to connect source and public static final int WEST public static final int NORTH public static final int EAST public static final int SOUTH drain nodes = 0; = 1; = 2; = 3; // }}} /** Width of the switch mesh */ public static int width = 4; /** Height of the switch mesh */ public static int height = 4; /** Size of the input buffers in switches */ public static int inputBufferSize; 18 /** Size of the output buffers in switches */ public static int outputBufferSize; /** Number of virtual lanes used */ public static int numberOfVLs; /** Number of ports on the source nodes */ public static int numberOfPortsOnSourceNodes; /** Number of ports on the drain nodes */ public static int numberOfPortsOnDrainNodes; public static java.text.DateFormat df = DateFormat.getTimeInstance(DateFormat.LONG); public static long realStartTime, realStopTime; /** Indicates whether we should display the gui */ public static boolean guiIsEnabled = false; // {{{ preInfo() public static void preInfo(){ realStartTime = System.currentTimeMillis(); Reporter.setStartTime(realStartTime); Reporter.println("Started simulation of "+Utils.formatLong(STOP_TIME)+ " time units. Time is "+ df.format(new Date(realStartTime))); Reporter.println("Press Crtl-C to stop simulation on the "+ "current time step."); } // }}} // {{{ postInfo() public static void postInfo(){ realStopTime = Reporter.stopTime(); // Won’t update this in Kernel if we are aborted with // Ctrl-C or equivalents, so we have to register it here // instead. if (realStopTime == 0) { realStopTime = System.currentTimeMillis(); } Reporter.getOutputStream().flush(); if (! guiIsEnabled) { // Print out some stats Reporter.println(); Reporter.println("Simulation statistics:"); Reporter.println("========================="+ "========================="); // Print out report for all source nodes for (int i=0; i < ibas.length; i++){ Reporter.println(ibas[i].report()); } // Print out report for drain nodes String report = null; for (int i=0; i < ibad.length; i++){ report = ibad[i].report(); if (report != null) { Reporter.println(report); } } 19 // Print out info about dropped packets Reporter.println(); HashMap dropped = IBAStatistics.getDroppedPacketStats(); int totalDropped = 0; if (dropped.keySet().size() > 0){ Iterator it = dropped.keySet().iterator(); while (it.hasNext()){ IBASourceNode node = (IBASourceNode) it.next(); totalDropped += ((Integer) dropped.get(node)).intValue(); } } } // Simulation summary Reporter.println(); Reporter.println("Finished simulation of "+ Utils.formatLong(kernel.currentTime())+ " time units. Time is "+ df.format(new Date(realStopTime))); long timeUsed = realStopTime - realStartTime; Reporter.println("Total time used: "+Utils.timeFormat(timeUsed)); Reporter.println(); } // }}} // {{{ public static void gui() public static void gui() { String localhost; try { localhost = InetAddress.getLocalHost().getHostName(); } catch (java.net.UnknownHostException uhe) { localhost = "localhost"; } JFrame f = new JFrame("GRUNS - GRand Unified Network Simulator "+ " - Running on "+localhost); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); /* StatusPanel panel = new StatusPanel(width*2 + (height-2)*2, // Source nodes width*2 + (height-2)*2, // Drain nodes width*2 + (height-2)*2, // Edge switches width * height, // Core switches realStartTime); */ int ibasLength, ibadLength, edgeLength, coreLength; ibasLength = (ibas != null) ? ibas.length : 0; ibadLength = (ibad != null) ? ibad.length : 0; edgeLength = (edgeIbaSw != null) ? edgeIbaSw.length : 0; coreLength = (coreIbaSw != null) ? coreIbaSw.length : 0; 20 StatusPanel panel = new StatusPanel(ibasLength, ibadLength, edgeLength, coreLength, realStartTime) ; f.getContentPane().add(panel); f.setSize(800,600); f.setVisible(true); Reporter.setGuiParams(panel, ibas, ibad, edgeIbaSw, coreIbaSw ); } // }}} // {{{ public static void main(String[] args) public static void main(String [] args){ // Initialize system properties IBAConstants.loadProperties(); // Do we want a gui? guiIsEnabled = Boolean.getBoolean("infiniband.GUI_ENABLED"); // Use the new kernel. int stopTime = Integer.getInteger("infiniband.STOP_TIME", STOP_TIME).intValue(); System.setProperty("infiniband.STOP_TIME", ""+stopTime); kernel = new NewKernel(stopTime); Reporter.setOutputStream(System.out); Reporter.setKernel(kernel); IBAStatistics.setKernel(kernel); // Print out some info before we start preInfo(); // Make sure there’s _some_ sensible output // if the simulation is aborted. Runtime.getRuntime().addShutdownHook(new Thread(){ public synchronized void run(){ Main.postInfo(); // Avoid returning an error value because Crtl-C // is pressed. // It’s perfectly legal to stop the simulation // before it’s finished. Runtime.getRuntime().halt(0); 21 }}); try{ IBAConnectionTools.setKernel(kernel); // Parse command line arguments for (int i=0; i < args.length; i++){ if (args[i].equals("--duplexGrid")){ duplexGrid(); break; } else if (args[i].equals("--smallPriTest")) { smallPriTest(); } else if (args[i].equals("--aBitBiggerPriTest")) { aBitBiggerPriTest(); } } }catch(RuntimeException e){ Reporter.println("RuntimeException occured!"); // Print out some info about the unit that crashed if (kernel.currentUnit() != null){ Reporter.println("kernel.currentUnit = "+ ((Reporting)kernel.currentUnit()).report()); } e.printStackTrace(Reporter.getOutputStream()); } } // }}} // {{{ public static void connectSourceAndDrain(...) public static void connectSourceAndDrain(int numberOfPortsOnSourceNodes, int numberOfVLs, int numberOfPortsOnDrainNodes, int addrPrefix, int addrSuffix, int currIndex, int edge){ int edgePortNumber = -1; switch (edge){ case WEST: edgePortNumber = 0; break; case NORTH: edgePortNumber = 1; break; case EAST: edgePortNumber = 2; break; case SOUTH: edgePortNumber = 3; break; default: throw new java.lang. IllegalArgumentException("Illegal edge number "+ "supplied. Value must be either "+ "WEST, NORTH, EAST or SOUTH."); } // Source node ibas[addrSuffix] = new IBASourceNode(numberOfPortsOnSourceNodes, numberOfVLs, "Source "+(addrPrefix)+"."+ (addrSuffix), kernel); ibas[addrSuffix].setOwnAddress((addrPrefix)+"."+(addrSuffix)); 22 // Drain node ibad[addrSuffix] = new IBADrainNode(numberOfPortsOnDrainNodes, numberOfVLs, "Drain "+(addrPrefix)+"."+ (addrSuffix), kernel); ibad[addrSuffix].setOwnAddress((addrPrefix)+"."+(addrSuffix)); // Edge switch edgeIbaSw[addrSuffix] = new IBASwitch(2,2,numberOfVLs,"Edge switch "+ addrSuffix,kernel); // Connect source and drain node to edge switch IBAConnectionTools.connect(ibas[addrSuffix], 0, edgeIbaSw[addrSuffix], 0); IBAConnectionTools.connect(edgeIbaSw[addrSuffix], 0 , ibad[addrSuffix], 0); // Connect edge switch to core switch IBAConnectionTools.connect(edgeIbaSw[addrSuffix], 1 , coreIbaSw[currIndex], edge); IBAConnectionTools.connect(coreIbaSw[currIndex], edge , edgeIbaSw[addrSuffix], 1); } // }}} // {{{ public static void duplexGrid() public static void duplexGrid(){ int numberOfVLs = 2; int inputBufferSize = 2 * Integer.parseInt( System.getProperty( "infiniband.EXP_PACKET_SIZE")); int outputBufferSize = 2 * Integer.parseInt( System.getProperty( "infiniband.EXP_PACKET_SIZE")); int numberOfEdgeSwitches = 2 * (width + (height - 2)); int numberOfSourceNodes = numberOfEdgeSwitches; int numberOfDrainNodes = numberOfEdgeSwitches; int int int int numberOfPortsOnSourceNodes = 1; numberOfPortsOnDrainNodes = 1; numberOfOutputPortsOnSwitches = 4; numberOfOutputPortsOnEdgeSwitches = 2; double fourKblocksPerPacket = Double.parseDouble(System. getProperty("infiniband.EXP_PACKET_SIZE")) / 4096; double ibaBlocksPerPacket = /*Math.ceil*/((Double. parseDouble(System. getProperty("infiniband.EXP_PACKET_SIZE")) 23 + 2) / Double. parseDouble(System. getProperty("infiniband.BLOCK_SIZE"))); int limitOfHighPriority; String pri = System.getProperty("infiniband.LIMIT_OF_HIGH_PRIORITY"); if (pri != null){ limitOfHighPriority = Integer.parseInt(System. getProperty("infiniband.LIMIT_OF_HIGH_PRIORITY")); }else{ limitOfHighPriority = 255; } // Simulation parameter summary if (! guiIsEnabled) { Reporter.println("limitOfHighPriority = "+limitOfHighPriority); Reporter.println("packet size = "+System. getProperty("infiniband.REAL_PACKET_SIZE")); Reporter.println("4 K blocks per packet = "+fourKblocksPerPacket); Reporter.println("IBA (64 B) blocks per packet = "+ ibaBlocksPerPacket); } // // init IBAConnectionTools.setKernel(kernel); // Build base mesh coreIbaSw = IBAConnectionTools.duplexGrid(width, height, numberOfVLs); edgeIbaSw = new IBASwitch[numberOfEdgeSwitches]; ibas = new IBASourceNode[numberOfSourceNodes]; ibad = new IBADrainNode[numberOfDrainNodes]; // Connect source and drain nodes to mesh // The prefix of the source and drain nodes’ addresses. int addrPrefix=1; // The suffix of the address on the source and drain nodes, // also the number of the edge switch, source and drain nodes. int addrSuffix = 0; // The index of the core switch we are connecting to int currIndex = 0; // Top row for (int i=0; i < width; i++){ currIndex = i; connectSourceAndDrain(numberOfPortsOnSourceNodes, numberOfVLs, numberOfPortsOnDrainNodes, addrPrefix, 24 addrSuffix, currIndex, NORTH); addrSuffix++; } // East (right) row for (int i=1; i < height-1; i++){ currIndex = (width*i) + (width-1); connectSourceAndDrain(numberOfPortsOnSourceNodes, numberOfVLs, numberOfPortsOnDrainNodes, addrPrefix, addrSuffix, currIndex, EAST); addrSuffix++; } // Bottom (south) row for (int i=0; i < width; i++){ currIndex = (width*height)-i-1; connectSourceAndDrain(numberOfPortsOnSourceNodes, numberOfVLs, numberOfPortsOnDrainNodes, addrPrefix, addrSuffix, currIndex, SOUTH); addrSuffix++; } // West (left) row for (int i=1; i < height-1; i++){ currIndex = (width*(height-i-1)); connectSourceAndDrain(numberOfPortsOnSourceNodes, numberOfVLs, numberOfPortsOnDrainNodes, addrPrefix, addrSuffix, currIndex, WEST); addrSuffix++; } // Paths in the net (from sources to drains) if (Boolean.getBoolean("infiniband.OPTIMISTIC_ROUTING")){ // Optimistic routing in the net, to achieve higher capacity, // at the risk of routing loops IBAConnectionTools.buildOptimisticRoutingTable(coreIbaSw, edgeIbaSw, width, height); } else { 25 // Normal routing in a grid/mesh (first vertical, then horizontal), // to avoid routing loops. IBAConnectionTools.buildDefaultRoutingTable(coreIbaSw, edgeIbaSw, width, height); } // To make packets leave the source nodes // we must add a priority to the packets sent from them. for (int i=0; i < ibas.length; i++){ for (int j=0; j < numberOfVLs; j++){ ibas[i].addHighPriority(0, j, 1); } } // Configure weights on output ports on the core and edge switches. // Makes the lower half of the VLs low priority, and the // upper half high priority. // Core for (int i=0; i < coreIbaSw.length; i++){ for (int j=0; j < numberOfOutputPortsOnSwitches; j++){ for (int k=0; k < numberOfVLs/2; k++){ coreIbaSw[i].addLowPriority(j, k, 1); } for (int k=numberOfVLs/2; k < numberOfVLs; k++){ coreIbaSw[i].addHighPriority(j, k, 1); } // limit of high priority coreIbaSw[i].setLimitOfHighPriority(j, limitOfHighPriority); } } // Edge for (int i=0; i < edgeIbaSw.length; i++){ for (int j=0; j < numberOfOutputPortsOnEdgeSwitches; j++){ for (int k=0; k < numberOfVLs/2; k++){ edgeIbaSw[i].addLowPriority(j, k, 1); } for (int k=numberOfVLs/2; k < numberOfVLs; k++){ edgeIbaSw[i].addHighPriority(j, k, 1); } // limit of high priority edgeIbaSw[i].setLimitOfHighPriority(j, limitOfHighPriority); } } // Source nodes // Set up which source nodes send to which address int usagePercent = 100; System.setProperty("infiniband.USAGE_PERCENTAGE", ""+usagePercent); // High pri traffic ibas[0].addDestinationAddress("1.4"); 26 ibas[0].addRoutingInterval("1.4", 0); ibas[0].setUsage(usagePercent); ibas[0].setSL(1); kernel.schedule(ibas[0], 0); /* ibas[9].addDestinationAddress("1.4"); ibas[9].addRoutingInterval("1.4", 0); ibas[9].setUsage(50); ibas[9].setSL(1); kernel.schedule(ibas[9], 0); */ /* // High pri traffic from 1.0 to everywhere for (int i=0; i < ibad.length; i++) { ibas[0].addDestinationAddress("1."+i); ibas[0].addRoutingInterval("1."+i, 0); } ibas[0].setUsage(usagePercent); ibas[0].setSL(1); kernel.schedule(ibas[0],0); */ /* // Hi pri background traffic from 1.1 to 1.4 ibas[1].addDestinationAddress("1.4"); ibas[1].addRoutingInterval("1.4", 0); ibas[1].setUsage(usagePercent); ibas[1].setSL(1); // Turn on kernel.schedule(ibas[1], 0); */ /* // Low pri background traffic from 1.9 to 1.4 ibas[9].addDestinationAddress("1.4"); ibas[9].addRoutingInterval("1.4", 0); ibas[9].setUsage(usagePercent); ibas[9].setSL(0); // Turn on kernel.schedule(ibas[9], 0); */ // Low pri background traffic from everywhere // to everywhere. //for (int i=0; i < ibas.length; i++) { for (int i=1; i < ibas.length; i++) { //if (i != 9) { //for (int i=0; i < ibas.length; i++) { for (int j=0; j < ibad.length; j++) { ibas[i].addDestinationAddress("1."+j); ibas[i].addRoutingInterval("1."+j, 0); } ibas[i].setUsage(usagePercent); ibas[i].setSL(0); // Turn on kernel.schedule(ibas[i], 0); //} } /* 27 // Low pri background traffic for (int i=1; i <= 3; i++) { //for (int i=3; i <= 3; i++) { ibas[i].addDestinationAddress("1.5"); ibas[i].addRoutingInterval("1.5", 0); ibas[i].setUsage(usagePercent); ibas[i].setSL(0); // Turn on kernel.schedule(ibas[i], 0); } */ /* for (int i=6; i <= 9; i++) { //for (int i=8; i <= 8; i++) { ibas[i].addDestinationAddress("1.4"); ibas[i].addRoutingInterval("1.4", 0); ibas[i].setUsage(usagePercent); ibas[i].setSL(0); // Turn on kernel.schedule(ibas[i], 0); } */ // Enable the gui if wanted if (guiIsEnabled) { gui(); } // Start simulation kernel.simulate(); } // }}} // {{{ public static void smallPriTest() public static void smallPriTest() { int numberOfVLs = 2; int inputBufferSize = 2 * Integer.parseInt( System.getProperty( "infiniband.EXP_PACKET_SIZE")); int outputBufferSize = 2 * Integer.parseInt( System.getProperty( "infiniband.EXP_PACKET_SIZE")); int numberOfSourceNodes = 2; int numberOfDrainNodes = 1; int numberOfCoreSwitches = 2; int numberOfPortsOnSourceNodes = 1; int numberOfPortsOnDrainNodes = 1; double fourKblocksPerPacket = Double.parseDouble(System. getProperty("infiniband.EXP_PACKET_SIZE")) / 4096; 28 double ibaBlocksPerPacket = /*Math.ceil*/((Double. parseDouble(System. getProperty("infiniband.EXP_PACKET_SIZE")) + 2) / Double. parseDouble(System. getProperty("infiniband.BLOCK_SIZE"))); int limitOfHighPriority; int usagePercent = 100; System.setProperty("infiniband.USAGE_PERCENTAGE", ""+usagePercent); String pri = System.getProperty("infiniband.LIMIT_OF_HIGH_PRIORITY"); if (pri != null){ limitOfHighPriority = Integer.parseInt(System. getProperty("infiniband.LIMIT_OF_HIGH_PRIORITY")); }else{ limitOfHighPriority = 255; } // Simulation parameter summary if (! guiIsEnabled) { Reporter.println("limitOfHighPriority = "+limitOfHighPriority); Reporter.println("packet size = "+System. getProperty("infiniband.REAL_PACKET_SIZE")); Reporter.println("4 K blocks per packet = "+fourKblocksPerPacket); Reporter.println("IBA (64 B) blocks per packet = "+ ibaBlocksPerPacket); } // // init IBAConnectionTools.setKernel(kernel); ibas = new IBASourceNode[numberOfSourceNodes]; ibad = new IBADrainNode[numberOfDrainNodes]; coreIbaSw = new IBASwitch[numberOfCoreSwitches]; // Source nodes for (int i=0; i < ibas.length; i++) { ibas[i] = new IBASourceNode(numberOfPortsOnSourceNodes, numberOfVLs, "Source 1."+i, kernel); ibas[i].setOwnAddress("1."+i); ibas[i].addDestinationAddress("2.0"); ibas[i].addRoutingInterval("2.0", 0); ibas[i].setUsage(usagePercent); // Service level differs ibas[i].setSL(i); // Make the packets leave the source nodes by adding a hi pri // entry to the output port. for (int j=0; j < numberOfVLs; j++) { ibas[i].addHighPriority(0, j, 1); } // Turn on kernel.schedule(ibas[i], 0); 29 } // Switches for (int i=0; i < coreIbaSw.length; i++) { coreIbaSw[i] = new IBASwitch(2,1,numberOfVLs, "Switch "+i, kernel); for (int j=0; j < numberOfVLs/2; j++){ coreIbaSw[i].addLowPriority(0, j, 1); } for (int j=numberOfVLs/2; j < numberOfVLs; j++){ coreIbaSw[i].addHighPriority(0, j, 1); } for (int j=0; j < numberOfDrainNodes; j++) { // limit of high priority coreIbaSw[i].setLimitOfHighPriority(j, limitOfHighPriority); // Routing coreIbaSw[i].addRoutingInterval("2."+j, 0); } } // Drain node for (int i=0; i < numberOfDrainNodes; i++) { ibad[i] = new IBADrainNode(1, numberOfVLs, "Drain 2."+i, kernel); } // Connect them IBAConnectionTools.connect(ibas[0], 0, coreIbaSw[0], 0); IBAConnectionTools.connect(ibas[1], 0, coreIbaSw[0], 1); IBAConnectionTools.connect(coreIbaSw[0], 0, coreIbaSw[1], 0); IBAConnectionTools.connect(coreIbaSw[1], 0, ibad[0], 0); // Enable the gui if wanted if (guiIsEnabled) { gui(); } // Start the show kernel.simulate(); } // }}} // {{{ public static void aBitBiggerPriTest() public static void aBitBiggerPriTest() { int numberOfVLs = 2; int inputBufferSize = 2 * Integer.parseInt( System.getProperty( "infiniband.EXP_PACKET_SIZE")); int outputBufferSize = 2 * Integer.parseInt( System.getProperty( "infiniband.EXP_PACKET_SIZE")); int numberOfSourceNodes = 2; int numberOfDrainNodes = 1; int numberOfCoreSwitches = 4; int numberOfPortsOnSourceNodes = 1; int numberOfPortsOnDrainNodes = 1; 30 double fourKblocksPerPacket = Double.parseDouble(System. getProperty("infiniband.EXP_PACKET_SIZE")) / 4096; double ibaBlocksPerPacket = /*Math.ceil*/((Double. parseDouble(System. getProperty("infiniband.EXP_PACKET_SIZE")) + 2) / Double. parseDouble(System. getProperty("infiniband.BLOCK_SIZE"))); int limitOfHighPriority; int usagePercent = 100; System.setProperty("infiniband.USAGE_PERCENTAGE", ""+usagePercent); String pri = System.getProperty("infiniband.LIMIT_OF_HIGH_PRIORITY"); if (pri != null){ limitOfHighPriority = Integer.parseInt(System. getProperty("infiniband.LIMIT_OF_HIGH_PRIORITY")); }else{ limitOfHighPriority = 255; } // Simulation parameter summary if (!guiIsEnabled) { Reporter.println("limitOfHighPriority = "+limitOfHighPriority); Reporter.println("packet size = "+System. getProperty("infiniband.REAL_PACKET_SIZE")); Reporter.println("4 K blocks per packet = "+fourKblocksPerPacket); Reporter.println("IBA (64 B) blocks per packet = "+ ibaBlocksPerPacket); } // // init IBAConnectionTools.setKernel(kernel); ibas = new IBASourceNode[numberOfSourceNodes]; ibad = new IBADrainNode[numberOfDrainNodes]; coreIbaSw = new IBASwitch[numberOfCoreSwitches]; // Source nodes for (int i=0; i < ibas.length; i++) { ibas[i] = new IBASourceNode(numberOfPortsOnSourceNodes, numberOfVLs, "Source 1."+i, kernel); ibas[i].setOwnAddress("1."+i); ibas[i].addDestinationAddress("2.0"); ibas[i].addRoutingInterval("2.0", 0); ibas[i].setUsage(usagePercent); // Service level differs ibas[i].setSL(i); // Make the packets leave the source nodes by adding a hi pri // entry to the output port. 31 for (int j=0; j < numberOfVLs; j++) { ibas[i].addHighPriority(0, j, 1); } // Turn on kernel.schedule(ibas[i], 0); } // Switches for (int i=0; i < coreIbaSw.length; i++) { coreIbaSw[i] = new IBASwitch(2,1,numberOfVLs, "Switch "+i, kernel); for (int j=0; j < numberOfVLs/2; j++){ coreIbaSw[i].addLowPriority(0, j, 1); } for (int j=numberOfVLs/2; j < numberOfVLs; j++){ coreIbaSw[i].addHighPriority(0, j, 1); } for (int j=0; j < numberOfDrainNodes; j++) { // limit of high priority coreIbaSw[i].setLimitOfHighPriority(j, limitOfHighPriority); // Routing coreIbaSw[i].addRoutingInterval("2."+j, 0); } } // Drain node for (int i=0; i < numberOfDrainNodes; i++) { ibad[i] = new IBADrainNode(1, numberOfVLs, "Drain 2."+i, kernel); } // Connect them IBAConnectionTools.connect(ibas[0], 0, coreIbaSw[0], 0); IBAConnectionTools.connect(ibas[1], 0, coreIbaSw[1], 0); IBAConnectionTools.connect(coreIbaSw[0], 0, coreIbaSw[2], 0); IBAConnectionTools.connect(coreIbaSw[1], 0, coreIbaSw[2], 1); IBAConnectionTools.connect(coreIbaSw[2], 0, coreIbaSw[3], 0); IBAConnectionTools.connect(coreIbaSw[3], 0, ibad[0], 0); // Enable the gui if wanted if (guiIsEnabled) { gui(); } // Start the show kernel.simulate(); } // }}} } 32 TestJavaPriQ import java.util.*; import no.uio.ifi.tools.JavaPriorityQueue; public class TestJavaPriQ { public static void main (String args[]) { List list = Arrays.asList(args); JavaPriorityQueue queue = new JavaPriorityQueue(list); System.out.println(queue); queue = new JavaPriorityQueue(); try { System.out.println(queue.removeFirst()); } catch (NoSuchElementException e) { System.out.println( "Received expected exception"); } queue.insert("Joy", 8); queue.insert("Joy", 8); queue.insert("Scott", 9); queue.insert("Sueltz", 5); queue.insert("Bill", 8); queue.insert("McNealy", 9); queue.insert("Patricia", 5); queue.insert("C.", 5); queue.insert("Papadopoulos", 4); queue.insert("Greg", 4); System.out.println(queue); queue.addAll(list); System.out.println(queue); while (queue.size() != 0) { System.out.println(queue.removeFirst()); } } } 33 no.uio.ifi.gruns.Kernel package no.uio.ifi.gruns; import no.uio.ifi.tools.*; /** *<p> * This is the heart of the simulator. *</p> * *<p> * It is responsible for scheduling scheduleable objects on * certain times, and keep a list of objects that wait to * be executed. When the given stop time has passed, * the kernel stops the simulation. */ public class Kernel implements Reporting{ /** * Holds a list of the units that are scheduled for excecution */ protected PriorityQueue scheduledUnits; /** * The current job to execute */ protected Scheduleable currentUnit; /** * The time to stop the simulation */ protected int stopTime; /** * The current time in the kernel */ protected int currentTime; /** * Returns the current time in the kernel */ public int currentTime(){ return this.currentTime; } /** * Returns the current running unit */ public Scheduleable currentUnit(){ return this.currentUnit; } /** * Returns the number of items in the queue of scheduled items. * For logging puurposes. */ public int itemQueueLength(){ 34 return scheduledUnits.getSize(); } /** For logging purposes, the number of times runCurrent() is called. */ protected int numRunCurrent = 0; /** For logging purposes, the number of times schedule() is called. */ protected int numSchedule = 0; public Kernel() {} public Kernel(int stopTime){ scheduledUnits = new SimplePriorityQueue(new ItemComparator()); this.stopTime = stopTime; } /** * Picks the next scheduleable object to execute */ protected Scheduleable pickNext(){ // Increase the current time to // be synchronized with the current running unit. if (scheduledUnits.getMinKey() != null){ currentTime = ((Integer) scheduledUnits.getMinKey()).intValue(); } return (Scheduleable) scheduledUnits.removeMinElement(); } /** * Executes the current scheduleable object. */ protected void runCurrent(){ // logging numRunCurrent++; /* // DEBUG Reporter.debugln("Time: "+currentTime+", "+ "Running "+((Unit)currentUnit).getID()); // */ currentUnit.act(); } /** * States whether an item is already scheduled. * To avoid unnecessary resceduling, e.g. in the IBASwitch. */ public boolean isScheduled(Scheduleable s){ return scheduledUnits.contains(s); } 35 /** * Schedules a scheduleable object for excecution * at at specified time. */ public void schedule(Scheduleable s, int time){ // Error if time is below current time if (time < currentTime){ Reporter.error("Kernel.schedule: "+ "Can’t schedule events back in time!"); } // Check if the object is already scheduled for running // on that time step, if not, schedule it. if (! scheduledUnits.contains(time, s)){ /* // DEBUG Reporter.debugln("Scheduling "+((Unit)s).getID()+ " on time "+time); // */ numSchedule++; scheduledUnits.insertItem(new Integer(time), s); } } /** * The main loop, runs the simulation. */ public void simulate(){ long tmpTime=0; while (currentTime < stopTime) { int prevTime = currentTime; // Picks the next and updates current time currentUnit = pickNext(); /* // DEBUG System.err.println("currentTime = "+currentTime+ ", currentUnit = "+((Unit)currentUnit).getID()); Utils.pause(); // */ // Break if there is nothing more to simulate if (currentUnit == null){ break; } // Break if we have exceeded time limit if (currentTime > stopTime) { // Register when we finished Reporter.setStopTime(System.currentTimeMillis()); break; } 36 // Progress monitor Reporter.proceed(currentTime); // Print out the time steps to stay in control with lots of // debugging info coming. /* if (prevTime != currentTime){ Reporter.println(); Reporter.println("Time step "+currentTime); Reporter.println("-----------------------"+ "---------------------------"); } // Print out info on current unit before running Reporter.println("current unit before = "+ ((currentUnit instanceof Reporting)?((Reporting)currentUnit).report(): currentUnit.toString())); */ runCurrent(); } } public String report return "Kernel"; } (){ // For benchmarking public long timeSpentPickingAndScheduling = 0; public long timeSpentRunningCurrent = 0; } 37 no.uio.ifi.gruns.Link package no.uio.ifi.gruns; /** * Represents a simplex physical link, i.e. communication * flows in just one direction. */ public class Link extends Unit implements ByteSender, ByteReceiver, Reporting, Scheduleable{ public Link(Kernel kernel){ super(kernel); //maxSize = 65536; // Just some number (64 K bytes) //maxSize = 32768; // Just some number (32 K bytes) //maxSize = 512; // Just some number maxSize = 1024; // Just some number inBuffer = new ByteBuffer(maxSize); outBuffer = new ByteBuffer(maxSize); numberOfBytesSent = numberOfBytesReceived = 0; sizesOfBlocksInTransit = new java.util.Vector(); } /** The bytes to send are put in this buffer */ protected ByteBuffer inBuffer; /** The bytes to receive are put in this buffer */ protected ByteBuffer outBuffer; /** * The maximum size of the input and output buffers. * The total number of bytes on the link can be twice * this size. */ protected int maxSize; /** * The upstream unit of this link, e.g. the unit * that will be calling {@link #sendByte} on this link */ protected OutputPort source; /** * The downstream unit of this link, e.g. the unit * that will be calling {@link #receiveByte} on this link */ protected InputPort sink; /** * Number of bytes sent on this link, i.e. that have been * put on the link for sending. They need not have been * propagated through the link. (just for logging purposes) */ protected int numberOfBytesSent; /** 38 * Number of bytes received from this link, i.e that is * received in the other end, and is no longer on the link * (just for logging purposes) */ protected int numberOfBytesReceived; /** * Number of bytes transferred on this link, i.e. that has * actually moved across the link. * (just for logging purposes) */ protected int numberOfBytesPropagated; /** * List of sizes of blocks received */ protected java.util.Vector sizesOfBlocksInTransit; /** * Sets the upstream unit of this link, e.g. the unit * that will be calling {@link #sendByte} on this link */ public void setSource(OutputPort port){ this.source = port; } /** * Gets the upstream unit of this link, e.g. the unit * that will be calling {@link #sendByte} on this link */ public OutputPort getSource(){ return source; } /** * Sets the downstream unit of this link, e.g. the unit * that will be calling {@link #receiveByte} on this link */ public void setSink(InputPort port){ this.sink = port; } /** * Gets the downstream unit of this link, e.g. the unit * that will be calling {@link #receiveByte} on this link */ public InputPort getSink(){ return sink; } /** Sends a byte on the link */ public void sendByte(byte b) throws LinkFullException{ if (inBuffer.isFull()){ throw new LinkFullException(ID+": Link.send: Trying to send to a full buffer! "+ "In buffer size = "+inBuffer.getSize()+ ", Out buffer size = "+outBuffer.getSize()+ ", Maximum buffer size = "+maxSize); } 39 inBuffer.put(b); // Update for logging numberOfBytesSent++; } /** receives a byte from the link */ public byte receiveByte() throws LinkEmptyException{ byte b; if (outBuffer.isEmpty()){ throw new LinkEmptyException(ID+": Link.receive: Trying to receive from "+ "empty buffer! "+ "Out buffer size = "+outBuffer.getSize()+ ", In buffer size = "+inBuffer.getSize()+ ", Maximum buffer size = "+maxSize); } b = outBuffer.get(); // Update for logging numberOfBytesReceived++; return b; } /** * Tells the link that we have just sent a block of this size * on it. To avoid moving e.g. 64 bytes in the next step if we have only put * 38 on the link. This is because the input port in the other end will just * remove 38 bytes from the link, and hence we will get a traffic jam in * our own output port. * * @param numBytesInBlock The number of bytes in the last block */ public void addBlock(int numBytesInBlock){ sizesOfBlocksInTransit.add(new Integer(numBytesInBlock)); } /** * States whether there is no more room for packets to * be input on the link */ public boolean isFull(){ return (inBuffer.isFull()); } /** * States whether there are no more packets to be output * from the link */ public boolean isEmpty(){ return (outBuffer.isEmpty()); } /** * Returns the size of the next block to transfer on * the link. */ private int getSizeOfNextBlock(){ Integer integer = (Integer)sizesOfBlocksInTransit.remove(0); 40 return integer.intValue(); } /** * Move one block (64 bytes) * from input buffer to output buffer. * Reschedule if there’s more data in the input buffer. */ public void act(){ int numberOfBytesMovedThisTurn = 0; int numberOfBytesToMoveThisTurn = getSizeOfNextBlock(); /* // DEBUG if (ID.equals("(Switch 7 -> Drain 0)")){ Reporter.debugln(ID+": Sending "+numberOfBytesToMoveThisTurn+" bytes"); } // */ for (int i=0; i < numberOfBytesToMoveThisTurn; i++){ if (inBuffer.isEmpty()){ // Actually no error, just no more data. // Reporter.error("Link.act: Input buffer is empty!"); break; } numberOfBytesMovedThisTurn++; if (outBuffer.isFull()){ Reporter.error("Link.act: Output buffer is full!"); } outBuffer.put(inBuffer.get()); numberOfBytesPropagated++; } // Make sure we don’t schedule the sink if there is // no data to read. if (numberOfBytesMovedThisTurn != 0){ kernel.schedule(sink, kernel.currentTime() + 1); } // // // // // // Reshcedule if there’s more data waiting to be moved. Not necessary, I think. if (!inBuffer.isEmpty()){ kernel.schedule(this, kernel.currentTime() +1); } } public String report(){ return("Link "+ ID+ ", # bytes sent = "+numberOfBytesSent+ ", # bytes rec. = "+numberOfBytesReceived+ ", inBufferSize = "+inBuffer.getSize()+ 41 ", outBufferSize = "+outBuffer.getSize() /* ", # bytes moved this turn = " +numberOfBytesMovedThisTurn */ ); } } 42 no.uio.ifi.gruns.Packet package no.uio.ifi.gruns; /** * Represents a packet on the network. * Consists of a series of bytes. */ public class Packet{ /** The packet itself */ protected byte [] packet; /** The size of the packet */ protected int size; /** The current byte number */ protected int currentByteNumber; protected static final int BYTE_SIZE = 8; /** Default constructor that does nothing */ public Packet(){ } /** * Creates a new packet with the content specified */ public Packet(byte [] content){ this.packet = content; size = content.length; } /** * Creates a new packet with the specified to-address * and the specified payload. * <br> */ public Packet(Address destination, byte[] payload){ int addrLength = destination.getSize()/BYTE_SIZE; byte[] content = new byte[addrLength + payload.length]; byte[] address = destination.getAddress(); // Place the address at the beginning of the packet for (int i=0; i < addrLength; i++){ content[i] = address[i]; } // Fill in the payload for (int i = 0; i < payload.length; i++){ content[i + addrLength] = payload[i]; } this.packet = content; this.size = content.length; } public Address getAddress(){ byte [] addr = { packet[0], packet[1] }; Address address = new Address(addr); return address; 43 } /** * Returns the total size of the packet, * including all headers and payload. */ public int getSize(){ return packet.length; } /** * Gets the next byte of this packet */ public byte getNextByte(){ return (currentByteNumber >= size) ? -1 : packet[currentByteNumber++]; } } 44 no.uio.ifi.gruns.PacketReceiver package no.uio.ifi.gruns; /** * All classes that are able to receive packets * should implement this interface. */ public interface PacketReceiver { /** * Receives a packet * @return The packet received * @param portNumber The physical output port number * to receive the packet on * @param VLNumber The virtual lane number * to receive the packet on */ public Packet receivePacket(int portNumber, int VLNumber); } 45 no.uio.ifi.gruns.RoutingTable package no.uio.ifi.gruns; import java.util.HashMap; import java.util.Iterator; /** * Represents the routing table for a switch */ public class RoutingTable implements Reporting{ public RoutingTable(){ routingTableEntries = new HashMap(); } /** The routing table entries for this routing table */ protected HashMap routingTableEntries; /** * Adds a possible output route for the supplied address * @param a The address to add a route for * @param outgoingPortNumber The port number of the route * @param outgoingVLNumber The virtual lane number of the route */ public void addRoute(Address a, int outgoingPortNumber, int outgoingVLNumber){ RoutingTableEntry entry = null; // Fetch the routing table entry for the specified address, // create a new one if it is not found. if (routingTableEntries.containsKey(a.getAddressAsString())){ entry = (RoutingTableEntry) routingTableEntries.get(a.getAddressAsString()); }else{ entry = new RoutingTableEntry(); routingTableEntries.put(a.getAddressAsString(), entry); } // Make a new Route Route r = new Route(); r.portNumber = outgoingPortNumber; r.VLNumber = outgoingVLNumber; // Add the new route to the routing table entry entry.addRoute(r); } /** * Deletes a possible output route for the supplied address * @param a The address to delete a route for * @param outgoingPortNumber The port number of the route * @param outgoingVLNumber The virtual lane number of the route */ public void deleteRoute(Address a, int outgoingPortNumber, int outgoingVLNumber){ 46 RoutingTableEntry entry = null; // Fetch the routing table entry for the specified address. if (routingTableEntries.containsKey(a.getAddressAsString())){ entry = (RoutingTableEntry) routingTableEntries.get(a.getAddressAsString()); } else{ // No need to remove a route that doesn’t exist, // so just return silently. return; } // Make a new dummy Route Route r = new Route(); r.portNumber = outgoingPortNumber; r.VLNumber = outgoingVLNumber; // Delete the route from the routing table entry entry.deleteRoute(r); } /** * Looks up an address in the routing table. * If multiple routes are found, multiple calls will return * the routes in turn, starting with the first route added. * * @return The Route to send the packet out on. * <tt>null</tt> if no route is found. * @param a The address to look up. */ public Route lookUp(Address a){ RoutingTableEntry entry = null; Route retVal = null; // Fetch the routing table entry for the specified address. if (routingTableEntries.containsKey(a.getAddressAsString())){ entry = (RoutingTableEntry) routingTableEntries.get(a.getAddressAsString()); } // Return null if no entry is found for the supplied address if (entry == null){ retVal = null; }else{ retVal = entry.getNextRoute(); } return retVal; } public String report(){ return "RoutingTable"; } } 47 no.uio.ifi.gruns.Scheduleable package no.uio.ifi.gruns; /** * All classes that can be scheduled to be run on a {@link no.uio.ifi.gruns.Kernel} * should implement this interface. */ public interface Scheduleable{ /** * Called by the simulator kernel whenever the scheduleable unit * is executed. */ public void act(); } 48 no.uio.ifi.gruns.Address package no.uio.ifi.gruns; /** * Represents the address of a packet with 16 bytes. * This corresponds to the 16-bit LID (Local Identifier) * in the InfiniBand Architecture. */ public class Address { // {{{ Protected members protected byte[] address; /** * Used to store 256 values at any range (e.g. 0 to 255) * pr. byte, not just -127 to 128. */ protected byte base = 0; /** * Used to store 256 values at any range (e.g. 0 to 255) * pr. byte, not just -127 to 128. */ protected byte offset = (byte) (0 - (base - (byte) Byte.MIN_VALUE ) ); // }}} // {{{ protected int getSize() /** * Returns the size of the address in bits */ protected int getSize(){ return 16; } // }}} // {{{ Constuctors // {{{ public Address(byte [] address /** * Makes a new address from the supplied byte array. */ public Address(byte [] address){ this.address = address; } // }}} // {{{ public Address(String address) /** * Translates the supplied String to a valid address * and stores it in this object. * <br> * The format should be on the form <tt>aaa.bbb</tt>, where * <b>aaa</b> and <b>bbb</b> are numbers between 0 and 255. */ public Address(String address){ this.address = stringToByteArray(address); } // }}} 49 // }}} // {{{ public byte[] getAddress() /** * Gets the address in its raw byte[] format. */ public byte[] getAddress(){ return address; } // }}} // {{{ public String getAddressAsString() /** * Gets the address in a more human-readable form */ public String getAddressAsString(){ return byteArrayToString(address); } // }}} // {{{ protected String byteArrayToString(byte[] address) /** * Converts a byte array to a more human-readable string */ protected String byteArrayToString(byte[] address){ StringBuffer sb = new StringBuffer(); byte[] addr = getAddress(); int addr1=0, addr2=0; // Do it manually! int MASK = 0x00000001; for (int i=0; i < 8; i++){ addr1 += (addr[0] & MASK); // Reporter.println("addr1 = "+addr1); MASK = (MASK << 1); } MASK = 0x00000001; for (int i=0; i < 8; i++){ addr2 += (addr[1] & MASK); // Reporter.println("addr2 = "+addr2); MASK = (MASK << 1); } sb.append(addr1); sb.append("."); sb.append(addr2); return sb.toString(); } // }}} // {{{ protected byte [] stringToByteArray(String address) /** * Converts a human-readable string to a valid byte[] address * The format should be on the form <tt>aaa.bbb</tt>, where * <b>aaa</b> and <b>bbb</b> are numbers between 0 and 255. */ protected byte [] stringToByteArray(String address){ 50 byte [] addr = new byte[2]; int separator = address.indexOf("."); addr[0] = (byte) Integer.parseInt(address.trim(). substring(0, separator)); addr[1] = (byte) Integer.parseInt(address.trim(). substring(separator + 1)); return addr; } // }}} // {{{ public String toString() public String toString(){ return getAddressAsString(); } // }}} } 51 no.uio.ifi.gruns.AddressVector package no.uio.ifi.gruns; import java.util.Vector; /** * Specialized form of Vector which holds Address objects. * All methods correspond to those of {@link java.util.Vector} */ public class AddressVector extends Vector{ // {{{ Constructors // {{{ public AddressVector() public AddressVector(){ super(); } // }}} // {{{ public AddressVector(int initialCapacity) public AddressVector(int initialCapacity){ super(initialCapacity); } // }}} // }}} // {{{ public Address[] toAddressArray() public Address[] toAddressArray(){ Address[] addresses = new Address[size()]; toArray(addresses); return addresses; } // }}} } 52 no.uio.ifi.gruns.ByteBuffer package no.uio.ifi.gruns; /** * This class represents a buffer in e.g. a source node, * a drain node, a link or a switch. */ public class ByteBuffer{ /** The actual buffer */ protected byte[] buffer; /** The actual size of the buffer */ protected int size; /** The maximum size of the buffer */ protected int maxSize; /** We get data from the tail of the buffer */ protected int tail; /** We put data at the head of the buffer */ protected int head; /** * Creates a byte buffer with the specified capacity. */ public ByteBuffer(int capacity){ size = 0; maxSize = capacity; buffer = new byte[capacity]; tail = 0; head = 0; } /** Puts data at the end of the buffer */ public void put(byte b){ if (size >= maxSize){ Reporter.error("ByteBuffer.put: "+ "Trying to put to a full buffer! "+ "Buffer size = "+size+ ", Maximum buffer size = "+maxSize); return; } buffer[head] = b; head = (head + 1) % maxSize; size++; } /** Gets data at the beginning of the buffer */ public byte get(){ byte b; if (size <= 0){ Reporter.error("ByteBuffer.get: "+ "Trying to receive from empty buffer! "+ "Buffer Size = "+size+ ", Maximum buffer size = "+maxSize); return (byte) -1; 53 } b = buffer[tail]; tail = (tail + 1) % maxSize; size--; return b; } /** Returns the data as a byte array */ public byte[] toByteArray(){ byte[] retVal = new byte[size]; for (int i=0; i < retVal.length; i++){ retVal[i] = buffer[i]; } return retVal; } /** Returns the size of the buffer */ public int getSize(){ return size; } /** Returns the total capacity of the buffer */ public int getCapacity(){ return maxSize; } /** Returns the remaining capacity of the buffer */ public int getRemainingCapacity(){ return maxSize - size; } /** States whether the buffer is full */ public boolean isFull(){ return (size == maxSize); } /** States whether the buffer is empty */ public boolean isEmpty(){ return (size == 0); } /** Clears the buffer content */ public void clear(){ for (int i=0; i < maxSize; i++){ buffer[i] = 0; } size=0; tail=0; head=0; } } 54 no.uio.ifi.gruns.ByteReceiver package no.uio.ifi.gruns; /** * All classes that receive single bytes should implement this interface. */ public interface ByteReceiver{ /** * Receives a single byte */ public byte receiveByte(); } 55 no.uio.ifi.gruns.DrainNode package no.uio.ifi.gruns; /** * <p> * This class represents a drain node in a network. Its task is to * receive packets from the network and do something with them. * </p> * * <p> * It has a set of input ports which it can receive from, and each of * these input ports provide one or more virtual lanes to * receive packets from. * </p> */ public class DrainNode extends Unit implements PacketReceiver, Scheduleable { /** The address of this drain node */ protected Address address; public DrainNode(Kernel kernel){ super(kernel); } /** The input ports to receive packets on */ protected InputPort [] inputPorts; /** Receives a packet */ public Packet receivePacket(int portNumber, int VLNumber){ return null; } /** * Processes a received packet. */ protected void processPacket(){ } public void act(){ } public String report(){ return "DrainNode"; } } 56 no.uio.ifi.gruns.LinkFullException package no.uio.ifi.gruns; public class LinkFullException extends RuntimeException{ public LinkFullException(){ super(); } public LinkFullException(String message){ super(message); } } 57 no.uio.ifi.gruns.OutputPort package no.uio.ifi.gruns; /** *<p> * Represents the output part of a physical port on a switch, a * source node, a drain node etc. It is used to send packets * from the unit. Each node/switch typically has many ports. *</p> * *<p> * There is also a counterpart, the {@link no.uio.ifi.gruns.InputPort}, * which is used to receive packets. *</p> */ public class OutputPort extends Unit implements Reporting, Scheduleable{ /** * The capacity of the buffers in the virtual lanes */ protected int bufferSize = 65536; /** * The port which this output port is sending to */ protected Link link; /** * The number of virtual lanes this port supports */ protected int numberOfVLs; /** * The virtual lane we are currently sending on */ protected int currentVL; /** * The packet currently under transmission */ protected Packet currentPacket; /** * The output buffers in this port. * There are {@link #numberOfVLs} output buffers. */ protected ByteBuffer buffer[]; public OutputPort(Kernel kernel){ super(kernel); } /** * Creates an output port with the supplied number of * virtual lanes * * @param numberOfVirtualLanes The number of virtual lanes to provide 58 */ public OutputPort(int numberOfVirtualLanes, Kernel kernel){ this(kernel); this.numberOfVLs = numberOfVirtualLanes; this.buffer = new ByteBuffer[numberOfVLs]; for (int i=0; i < numberOfVLs; i++){ buffer[i] = new ByteBuffer(bufferSize); } currentVL = -1; ID = ""; } /** * Connects this output port to a link */ public void attach(Link link){ this.link = link; link.setSource(this); } /** * Sends a single packet on the supplied virtual lane number. */ public void sendPacket(Packet p, int VLNumber){ int i; for (i = 0; i < p.getSize() && !buffer[VLNumber].isFull(); i++){ buffer[VLNumber].put(p.getNextByte()); } // Error message if error sending packet if (i < p.getSize()){ Reporter.error("OutputPort.sendPacket("+p+ ", "+VLNumber+"): "+ "Not enough buffer space left!"); Reporter.error("\n"); } } /** * <p> * Sends a single packet on the link (one byte per call). * If there’s not enough time to send the whole packet * in this time unit, it reschedules itself on the next time unit. * </p> * <p> * No default implementation. * </p> */ public void act(){ } public String report(){ 59 return "OutputPort"; } } 60 no.uio.ifi.gruns.PacketSender package no.uio.ifi.gruns; /** * All classes that are able to send packets * should implement this interface. */ public interface PacketSender { /** * Sends a packet * @param p The packet to send * @param portNumber The physical output port number to send the packet on * @param VLNumber The virtual lane number to send the packet on */ public void sendPacket(Packet p, int portNumber, int VLNumber); } 61 no.uio.ifi.gruns.Reporter package no.uio.ifi.gruns; import java.io.PrintStream; import no.uio.ifi.gruns.infiniband.*; /** * Used to report from all the classes in the simulator. */ public final class Reporter{ /** * Prints an error message */ public static void error(String message){ out.println("Error: "+message); // An error is serious enough to abort throw new RuntimeException(message); } /** * Prints the report from the supplied Reporting unit */ public static void report(Reporting unit){ out.println(unit.report()); } /** * Prints a message */ public static void print(String message){ out.print(message); } /** * Prints an object by calling the appropriate toString() method. */ public static void print(Object o) { out.print(o); } /** * Prints a message (with ending newline) */ public static void println(String message){ out.println(message); } /** * Prints an object by calling the appropriate toString() method. */ public static void println(Object o) { out.println(o); } /** * Prints a newline 62 */ public static void println(){ out.println(); } // JUST CHANGE THESE TO DO NOTHING TO TEMPORARILY TURN OFF DEBUGGING /** * Prints a debug message. */ public static void debug(String message) { out.print("DEBUG: Time "+kernel.currentTime()+": "+message); } /** * Prints an object by calling the appropriate toString() method. */ public static void debug(Object o) { out.print("DEBUG: Time "+kernel.currentTime()+": "+o); } /** * Prints a debug message (with ending newline) */ public static void debugln(String message){ out.println("DEBUG: Time "+kernel.currentTime()+": "+message); } /** * Prints an object by calling the appropriate toString() method. */ public static void debugln(Object o) { out.println("DEBUG: Time "+kernel.currentTime()+": "+o); } /** * Prints a debug newline */ public static void debugln(){ out.println("DEBUG: Time "+kernel.currentTime()+": "); } /** * Sets the outputstream we write error messages to. * e.g: *<ul> * <li>Reporter.setOutputStream(new PrintStream( * new FileOutputStream("filename")))</li> * <li>Reporter.setOutputStream(System.out)</li> *</ul> */ public static void setOutputStream(PrintStream stream){ out = stream; } public static PrintStream getOutputStream(){ return out; } /** 63 * Just so that the reporter should have access to current time etc. */ public static void setKernel(Kernel k){ kernel = k; } /** * Gui */ public static void setGuiParams( no.uio.ifi.gruns.gui.StatusPanel p, no.uio.ifi.gruns.infiniband.IBASourceNode[] sources, no.uio.ifi.gruns.infiniband.IBADrainNode[] drains, no.uio.ifi.gruns.infiniband.IBASwitch[] edges, no.uio.ifi.gruns.infiniband.IBASwitch[] cores ) { statusPanel = p; sourceNodes = sources; drainNodes = drains; edgeSwitches = edges; coreSwitches = cores; } /** * Indicats that we have proceeded some steps in our simuation. * could be changed to update a JProgressbar, e.g, hence the * general method, not just a "print(".")" from the Kernel. */ public static void proceed(int currentTime){ acc += (currentTime - previousTime); acc2 += (currentTime - previousTime); // Display status in gui if (statusPanel != null && acc2 >= 50) { statusPanel.setTime(kernel.currentTime()); if (sourceNodes != null) { for (int i=0; i < sourceNodes.length; i++) { statusPanel.setSourceStats(i, sourceNodes[i].getSentPackets(), sourceNodes[i].getWaitingPackets() ); } } if (drainNodes != null) { no.uio.ifi.gruns.infiniband.IBAStatistics.IBADrainNodeStats stats = null; int total = 0; for (int i=0; i < drainNodes.length; i++) { stats = no.uio.ifi.gruns.infiniband.IBAStatistics. getReceivedPacketStats(drainNodes[i]); if (stats != null) { statusPanel.setDrainStats(i, stats); } } } if (edgeSwitches != null) { for (int i=0; i < edgeSwitches.length; i++) { statusPanel.setEdgeStats(i, edgeSwitches[i].getTotalNumberOfPacketsSent() 64 ); } } if (coreSwitches != null) { for (int i=0; i < coreSwitches.length; i++) { statusPanel.setCoreStats(i, coreSwitches[i].getTotalNumberOfPacketsSent() ); } } statusPanel.setQueueLen(kernel.itemQueueLength()); statusPanel.updateMemUsage(); acc2 = 0; } if (statusPanel == null) { // Print a "." for every time step for (int i=0; i < (currentTime - previousTime); i++){ // Just for debugging //out.print(currentTime+"\n"); //out.flush(); out.print("."); out.flush(); } } // Prints a message for approx. every 5000 time steps. if (acc >= 5000){ if (statusPanel == null) { out.println("Simulating time step: "+Utils.formatInt(currentTime)+"."); } // Make sure that the garbage collector gets to run every // once in a while System.gc(); acc = 0; } previousTime = currentTime; } public static void setStartTime(long time) { realStartTime = time; } public static long startTime() { return realStartTime; } public static void setStopTime(long time) { realStopTime = time; } public static long stopTime() { return realStopTime; } 65 /** * The stream to print messages to */ private static PrintStream out; /** * The kernel */ private static Kernel kernel; /** The previous time */ private static int previousTime; /** To tell if we are to print out a wordy status msg.*/ private static int acc; /** To tell if we are to update the gui*/ private static int acc2; /** * For displaying results inline */ protected static no.uio.ifi.gruns.gui.StatusPanel statusPanel = null; /** * For displaying results inline */ protected static no.uio.ifi.gruns.infiniband.IBASourceNode[] sourceNodes = null; /** * For displaying results inline */ protected static no.uio.ifi.gruns.infiniband.IBADrainNode[] drainNodes = null; /** * For displaying results inline */ protected static no.uio.ifi.gruns.infiniband.IBASwitch[] edgeSwitches = null; /** * For displaying results inline */ protected static no.uio.ifi.gruns.infiniband.IBASwitch[] coreSwitches = null; /** * For registering when we started */ protected static long realStartTime; /** * For registering when we stopped */ protected static long realStopTime; // initialization static { realStartTime = 0; realStopTime = 0; 66 } } 67 no.uio.ifi.gruns.Route package no.uio.ifi.gruns; /** * This class represents a pair of (portNumber, VLNumber), * and is used in routing tables (more specifically in * {@link no.uio.ifi.gruns.RoutingTableEntry}. */ public class Route implements Reporting { /** The physical port number */ public int portNumber; /** The virtual lane number */ public int VLNumber; public String report(){ return ("Route: Port number: "+ this.portNumber+ ", virtual lane number: "+ this.VLNumber); } } 68 no.uio.ifi.gruns.SourceNode package no.uio.ifi.gruns; /** * <p> * This class represents a sourcenode in a network. Its task is to * generate packets and send them out on the network. * </p> * * <p> * It has a set of output ports which it can send to, and each of * these output ports provide one or more virtual lanes to * send packets on. * </p> * * <p> * Subclasses of this node can be made to have the source node * make other kinds of packets etc. * </p> */ public class SourceNode extends Unit implements PacketSender, Reporting, Scheduleable{ /** The output ports to send packets on */ protected OutputPort [] outputPorts; /** The addresses that this source node produces packets for */ protected AddressVector addresses; /** The address of this source node */ protected Address address; /** * Creates a new source node. * * @param numberOfOutputPorts The number of output ports * on this source node * @param numberOfVirtualLanes The number of virtual * lanes per output port */ public SourceNode(int numberOfOutputPorts, int numberOfVirtualLanes, Kernel kernel){ this(numberOfOutputPorts, numberOfVirtualLanes, "", kernel); } /** * Creates a new source node. * * @param numberOfOutputPorts The number of output ports * on this source node * @param numberOfVirtualLanes The number of virtual lanes * per output port * @param ID A String to identify the source node */ public SourceNode(int numberOfOutputPorts, int numberOfVirtualLanes, 69 String ID, Kernel kernel){ super(kernel); this.outputPorts = new OutputPort[numberOfOutputPorts]; for (int i = 0; i < numberOfOutputPorts; i++){ outputPorts[i] = new OutputPort(numberOfVirtualLanes, kernel); } addresses = new AddressVector(); this.ID = ID; } /** * Attaches a link to this source node on the * supplied output port number */ public void attach(Link l, int outputPortNumber) throws IllegalArgumentException { if (outputPorts == null){ throw new IllegalArgumentException("This source node "+ "has no output ports."); } if (outputPortNumber < 0 || outputPortNumber > outputPorts.length){ throw new IllegalArgumentException("Output port number "+ "out of range. Must be"+ "between 0 and "+ outputPorts.length); } outputPorts[outputPortNumber].attach(l); } /** * Adds a destination address to the list of addresses * that this source node produces packets for. */ public void addDestinationAddress(Address destination) { addresses.add(destination); } /** * Sends a packet * @param p The packet to send * @param portNumber The physical output port number to send the packet on * @param VLNumber The virtual lane number to send the packet on */ public void sendPacket(Packet p, int portNumber, int VLNumber){ outputPorts[portNumber].sendPacket(p, VLNumber); // Schedule the output port so that the packet moves on kernel.schedule(outputPorts[portNumber], kernel.currentTime() + 1); } /** * Makes a packet with a test message. */ protected Packet makePacket(){ Address a = RandomAddress.nextAddress(addresses.toAddressArray()); 70 Packet p = new Packet(a, ("Test packet from "+ID).getBytes()); return p; } public void act(){ Packet p = makePacket(); sendPacket(p, 0, 0); } public String report(){ return "SourceNode: "+ID; } } 71 no.uio.ifi.gruns.Unit package no.uio.ifi.gruns; /** * Just a parent class for all units that exist in * the simulator, to have some common methods in all * the units. */ public abstract class Unit { public Unit(Kernel kernel){ this.kernel = kernel; } /** Sets the ID of this unit */ public void setID(String ID){ this.ID = ID; } /** Gets the ID of this unit */ public String getID(){ return ID; } /** To identify this unit */ public String ID; /** * The simulator kernel */ protected Kernel kernel; } 72 no.uio.ifi.gruns.ByteSender package no.uio.ifi.gruns; /** * All classes that sends single bytes should implement this interface. */ public interface ByteSender{ /** * Send a single byte */ public void sendByte(byte b); } 73 no.uio.ifi.gruns.InputPort package no.uio.ifi.gruns; /** *<p> * Represents the input part of a physical port on a switch, a * source node, a drain node etc. It is used to receive packets * to the unit. Each node/switch typically has many ports. *</p> * *<p> * There is also a counterpart, the {@link no.uio.ifi.gruns.OutputPort}, * which is used to send packets. *</p> */ public abstract class InputPort extends Unit implements Reporting, Scheduleable{ public InputPort(Kernel kernel){ super(kernel); } /** * Creates an input port with the supplied number of * virtual lanes * * @param numberOfVirtualLanes The number of virtual lanes to provide */ public InputPort(int numberOfVirtualLanes, Kernel kernel){ this(kernel); this.numberOfVLs = numberOfVirtualLanes; this.buffer = new ByteBuffer[numberOfVLs]; for (int i=0; i < numberOfVLs; i++){ buffer[i] = new ByteBuffer(bufferSize); } currentVL = -1; ID = ""; } /** * The capacity of the buffers in the virtual lanes. * Currently set to 1 kB. */ protected int bufferSize = 1024; // 1 kB buffer on each VL /** * The link which this input port is receiving from */ protected Link link; /** * The number of virtual lanes this port supports */ protected int numberOfVLs; 74 /** * The number of virtual lanes this port supports */ public int numberOfVLs(){ return numberOfVLs; } /** * The virtual lane we are currently receiving from */ protected int currentVL; /** * The packet currently being received */ protected Packet currentPacket; /** * The output buffers in this port. * There are {@link #numberOfVLs} output buffers. */ protected ByteBuffer buffer[]; /** * Connects this input port to a link */ public void attach(Link link){ this.link = link; link.setSink(this); } /** * Receives a single packet from the supplied virtual lane number. * Called from the unit (e.g. drainnode, switch) that this * input port is connected to. DON’T USE THIS! * <b>No default implementation</b> */ public abstract Packet receivePacket(int VLNumber); /** * Receives a single packet. * Called from the unit (e.g. drainnode, switch) that this * input port is connected to. * <b>No default implementation</b> */ public abstract Packet receivePacket(); public abstract void act(); public String report(){ return "InputPort"; } } 75 no.uio.ifi.gruns.LinkEmptyException package no.uio.ifi.gruns; public class LinkEmptyException extends RuntimeException{ public LinkEmptyException(){ super(); } public LinkEmptyException(String message){ super(message); } } 76 no.uio.ifi.gruns.NewKernel package no.uio.ifi.gruns; import no.uio.ifi.tools.*; /** *<p> * This is the heart of the simulator, the new version using * JavaPriorityQueue, which is much faster. *</p> * *<p> * It is responsible for scheduling scheduleable objects on * certain times, and keep a list of objects that wait to * be executed. When the given stop time has passed, * the kernel stops the simulation. */ public class NewKernel extends Kernel{ /** * Holds a list of the units that are scheduled for excecution */ protected JavaPriorityQueue scheduledUnits; /** * Returns the number of items in the queue of scheduled items. * For logging puurposes. */ public int itemQueueLength(){ return scheduledUnits.size(); } public NewKernel(int stopTime){ scheduledUnits = new JavaPriorityQueue(); this.stopTime = stopTime; } /** * Picks the next scheduleable object to execute */ protected Scheduleable pickNext(){ Scheduleable s; // Increase the current time to // be synchronized with the current running unit. currentTime = scheduledUnits.minValue(); s = (Scheduleable) scheduledUnits.removeFirst(); return s; } /** * States whether an item is already scheduled. * To avoid unnecessary resceduling, e.g. in the IBASwitch. */ public boolean isScheduled(Scheduleable s){ return scheduledUnits.indexOf(s) != -1; 77 } /** * Schedules a scheduleable object for excecution * at at specified time. */ public void schedule(Scheduleable s, int time){ // Error if time is below current time if (time < currentTime){ Reporter.error("NewKernel.schedule: "+ "Can’t schedule events back in time!"); } // The JavaPriorityQueue will not schedule an item twice on the // same time step. It’s a feature, not a bug... /* // DEBUG Reporter.debugln("Scheduling "+((Unit)s).getID()+ " on time "+time); // */ numSchedule++; scheduledUnits.insert(s, time); } public String report (){ return "NewKernel"; } // For benchmarking public long timeSpentPickingAndScheduling = 0; public long timeSpentRunningCurrent = 0; } 78 no.uio.ifi.gruns.RandomAddress package no.uio.ifi.gruns; import java.util.Random; /** * Generates random addresses */ public class RandomAddress{ static Random random = new Random(); /** * Sets the seed used to return the random addresses */ public static void setSeed(int seed){ random.setSeed(seed); } /** * Returns one of the addresses supplied at random */ public static Address nextAddress(Address [] addresses){ return addresses[random.nextInt(addresses.length)]; } } 79 no.uio.ifi.gruns.Reporting package no.uio.ifi.gruns; public interface Reporting { /** * Reports some information about this unit. */ public String report(); } 80 no.uio.ifi.gruns.RoutingTableEntry package no.uio.ifi.gruns; import java.util.ArrayList; /** * Represents the entry of one address in the routing table. * Contains a number of possible routes for this address. */ public class RoutingTableEntry { /** * Keeps track of which route to return the next time */ protected int nextRouteIndex; /** * The possible routes of this address */ protected ArrayList routes; public RoutingTableEntry(){ nextRouteIndex = 0; routes = new ArrayList(); } /** * Adds a route to this address */ public void addRoute(Route r){ routes.add(r); } /** * Deletes a route from this address */ public void deleteRoute(Route r){ int index = -1; if ( (index = routes.indexOf(r)) >= 0 ){ routes.remove(index); } } /** * Gets the next route to send the packet out on. */ public Route getNextRoute(){ Route retVal = (Route) routes.get(nextRouteIndex); nextRouteIndex = ( nextRouteIndex + 1 ) % routes.size(); return retVal; } } 81 no.uio.ifi.gruns.Switch package no.uio.ifi.gruns; /** * Represents a switch, which receives packets on input ports, * looks up the address in the routing table and forwards the * packet on the correct output link. */ public class Switch extends Unit implements Scheduleable { public Switch(Kernel kernel){ super(kernel); } /** * Processes the packet */ protected void processPacket(Packet p){ } /** * Forwards the packet */ protected void forwardPacket(Packet p, int portNumber, int VLNumber) { } public String report(){ return "Switch"; } public void act(){ } } 82 no.uio.ifi.gruns.Utils package no.uio.ifi.gruns; import java.text.NumberFormat; import java.util.Locale; /** * This class contains some common methods that * can be useful from any class */ public class Utils { private static final int BITS_PER_BYTE = 8; private static int MASK = 0x80; private static final NumberFormat numberFormat; private static final Locale locale; static{ locale = Locale.ENGLISH; numberFormat = NumberFormat.getInstance(locale); } /** * Prints a byte[] as a bit string (of 1’s and 0’s). * Prints four bytes per line if more that four bytes. * @param b The bytes to print */ public static void printAsBitString(byte[] b){ Reporter.println(asBitString(b)); } /** * Returns a byte[] as a bit string (of 1’s and 0’s). * Prints four bytes per line if more that four bytes. * @param b The bytes to return as a bit string * @return a bit string of the bytes supplied. */ public static String asBitString(byte[] b){ StringBuffer retVal = new StringBuffer(); for (int i=0; i < b.length; i++){ retVal.append(asBitString(b[i])); retVal.append(" "); if( (i + 1) % 4 == 0) retVal.append("\n"); } // Strip off last character if it is a space. return retVal.toString().trim(); } /** * Returns a byte as a bit string (of 1’s and 0’s). * @param b The byte to return as a bit string * @return a bit string of the byte supplied. */ 83 public static String asBitString(byte b){ StringBuffer retVal = new StringBuffer(); MASK = 0x80; for (int j=0; j < BITS_PER_BYTE; j++){ if ( (b & MASK) != 0){ retVal.append("1"); }else { retVal.append("0"); } MASK = MASK >> 1; } return retVal.toString().trim(); } /** * Returns a String with large number on a more readable form. */ public static String formatInt(int number){ return numberFormat.format(number); } /** * Returns a String with large number on a more readable form. */ public static String formatLong(long number){ return numberFormat.format(number); } /** * Returns a String with a double on a more readable form. */ public static String formatDouble(double number){ return numberFormat.format(number); } /** * Prints <i>"Press Enter to continue!"</i> and * waits for keyboard press <Enter> before we continue. */ public static void pause(){ Reporter.print("Press Enter to continue!"); Reporter.getOutputStream().flush(); try{ new java.io.BufferedReader( new java.io.InputStreamReader(System.in)).readLine(); }catch(Exception e) { Reporter.println("Error pausing!"); e.printStackTrace(Reporter.getOutputStream()); } } /** * Parses a string with an address range and returns an array * containing each address in the range. * The format of the string can be one of the following, * where aaa, bbb, ccc and ddd represents integers between 0 and 255: * <table> 84 * <tr><td>aaa.bbb</td><td>Adds the specified address</tr> * <tr><td>aaa.bbb-ccc.ddd</td><td>Adds addresses ranging from * aaa.bbb to ccc.ddd, inclusive.</td></tr> * </table> * @param range A string describing the address range. */ public static Address[] getAddresses(String range){ AddressVector retVal = new AddressVector(); int dashIndex = range.indexOf(’-’); // No range specified, just add the address if (dashIndex == -1){ Address[] a = { new Address(range)}; return a; } else{ String startAddr = range.substring(0, dashIndex); String endAddr = range.substring(dashIndex+1); int[] startAddress = { Integer. parseInt(startAddr. substring(0, range.indexOf(’.’))), Integer. parseInt(startAddr. substring(range.indexOf(’.’)+1)) }; int[] endAddress = { Integer. parseInt(endAddr. substring(0, range.indexOf(’.’))), Integer. parseInt(endAddr. substring(range.indexOf(’.’)+1)) } ; int[] currentAddress = new int[2]; // Check the input parameters // Range check checkAddress(startAddr); checkAddress(endAddr); // Start must be smaller than end if ( (endAddress[0] < startAddress[0]) || ( (endAddress[0] == startAddress[0]) && (endAddress[1] < startAddress[1]) ) ) { throw new IllegalArgumentException("\nStart address "+ "must be smaller than "+ "end address. "+ startAddr+" is larger "+ "than "+endAddr+"."); } // Loop through all the addresses and add them int limit = 255; currentAddress[1] = startAddress[1]; for (currentAddress[0] = startAddress[0]; currentAddress[0] <= endAddress[0]; currentAddress[0]++){ 85 // Adjust the limit of the last part of the address if // the last prefix is reached if (currentAddress[0] == endAddress[0]){ limit = endAddress[1]; } while( currentAddress[1] <= limit ){ retVal.add(new Address(currentAddress[0]+ "."+currentAddress[1])); currentAddress[1]++; } // Reset last part of address for next run currentAddress[1] = 0; } } return retVal.toAddressArray(); } /** * Checks an address on string form for validity */ private static void checkAddress(String address) throws IllegalArgumentException { int[] addr = { Integer. parseInt(address. substring(0, address.indexOf(’.’))), Integer. parseInt(address. substring(address.indexOf(’.’)+1)) } ; if (addr[0] < 0 || addr[0] > 255){ throw new IllegalArgumentException("Address parts must be "+ "in the range 0 to 255. "+ addr[0]+" is out of range."); } if (addr[1] < 0 || addr[1] > 255){ throw new IllegalArgumentException("Address parts must be "+ "in the range 0 to 255. "+ addr[1]+" is out of range."); } } /** * Converts a time in milliseconds to a string on the form: * hh:mm:ss.msc */ public static String timeFormat(long time){ StringBuffer retVal = new StringBuffer(); long long long long millis = time; hours = millis / (60 * 60 * 1000); minutes = (millis - (hours * 60 * 60 * 1000) ) / (60 * 1000); seconds = (millis - (minutes * (60 * 1000)) (hours * 60 * 60 * 1000) ) / 1000; 86 //Subtract the millis used up in hours, //mins and seconds from the milli time. millis = time - (hours * 60 * 60 * 1000) (minutes * (60 * 1000)) - (seconds * 1000); retVal.append(hours < 10 ? "0" : ""); retVal.append(hours + ":"); retVal.append(minutes < 10 ? "0" : ""); retVal.append(minutes+ ":"); retVal.append(seconds < 10 ? "0" : ""); retVal.append(seconds + "."); retVal.append(millis < 100 ? "0" : ""); retVal.append(millis < 10 ? "0" : ""); retVal.append(millis); return retVal.toString(); } } 87 no.uio.ifi.gruns.gui.CustomTableModel /** * Used to display the simulation data in a table */ package no.uio.ifi.gruns.gui; import javax.swing.table.*; public class CustomTableModel extends AbstractTableModel { protected Object[][] rowData; protected Object[] columnNames; public CustomTableModel(int numRows, int numCols) { this.rowData = new Object[numRows][numCols]; this.columnNames = new Object[numCols]; } public CustomTableModel(Object[][] rowData, Object[] columnNames) { this.rowData = rowData; this.columnNames = columnNames; } public String getColumnName(int col) { return columnNames[col].toString(); } public int getRowCount() { return rowData.length; } public int getColumnCount() { return columnNames.length; } public Object getValueAt(int row, int col) { return rowData[row][col]; } public boolean isCellEditable(int row, int col) { return false; } public void setValueAt(Object value, int row, int col) { rowData[row][col] = value; fireTableCellUpdated(row, col); } public int rowOfKey(Object key) { int retVal = -1; for (int i=0; i < rowData.length; i++) { if (rowData[i][0].equals(key)) { retVal = i; } } return retVal; } public int colOfKey(Object key) { int retVal = -1; for (int i=0; i < columnNames.length; i++) { if (columnNames[i].equals(key)) { 88 retVal = i; } } return retVal; } public String toString() { StringBuffer retVal = new StringBuffer(); for (int i=0; i < columnNames.length; i++) { retVal.append(columnNames[i]); retVal.append(";"); } retVal.append("\n"); for (int i=0; i < rowData.length; i++) { for (int j=0; j < rowData[i].length-1; j++) { retVal.append(rowData[i][j]); retVal.append(";"); } // No ";" on the end of the line retVal.append(rowData[i][rowData[i].length-1]); retVal.append("\n"); } return retVal.toString(); } } 89 no.uio.ifi.gruns.gui.StatusPanel /* * StatusPanel.java * * Created on 18. desember 2002, 19:38 */ package no.uio.ifi.gruns.gui; import no.uio.ifi.gruns.*; import no.uio.ifi.gruns.infiniband.*; import no.uio.ifi.tools.*; import import import import java.awt.*; javax.swing.*; javax.swing.border.*; javax.swing.table.*; import java.util.Properties; import java.util.Enumeration; import java.util.*; /** * * @author erikbra */ public class StatusPanel extends javax.swing.JPanel protected protected protected protected final final final final static static static static int int int int { BORDER_LEFT = 15; BORDER_RIGHT = 15; BORDER_TOP = 15; BORDER_BOTTOM = 15; protected final static int addrPrefix = 1; JTabbedPane tabs; protected BorderLayout layout; protected JSplitPane summary; //protected JPanel sources,drains,edges,cores; protected JPanel north; protected CustomTableModel paramsModel, counterModel, sourcesModel, drainsModel, edgesModel, coresModel; protected protected protected protected JLabel JLabel JLabel JLabel timeStepLabel; realTimeLabel; speedLabel; avgSpeedLabel; protected JLabel queueLenLabel; protected JLabel allocatedMemLabel; protected JLabel freeMemLabel; protected JLabel maxMemLabel; protected protected protected protected JLabel[][] sourceNodes; JLabel[] drainNodes; JLabel[][] edgeSwitches; JLabel[][] coreSwitches; protected long startTime; protected int lastTimeStep; protected long lastRealTime; 90 /** Used to calculate effective usage percentage on switches’ ports. */ protected int currentTimeStep = 0; /** Creates a new instance of StatusPanel */ public StatusPanel(int numSourceNodes, int numDrainNodes, int numEdgeSwitches, int numCoreSwitches, long startTime) { tabs = new JTabbedPane(); layout = new BorderLayout(); this.setLayout(layout); this.add(tabs, BorderLayout.CENTER); north = new JPanel(); north.setLayout(new FlowLayout()); JButton dumpAll; north.add(dumpAll = new JButton("Dump all data")); dumpAll.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { dumpData(); }}); JButton dumpParams; north.add(dumpParams = new JButton("Dump params")); dumpParams.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { dumpParams(); }}); JButton dumpCounter; north.add(dumpCounter = new JButton("Dump counter")); dumpCounter.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { dumpCounter(); }}); JButton dumpSources; north.add(dumpSources = new JButton("Dump sources")); dumpSources.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { dumpSources(); }}); JButton dumpDrains; north.add(dumpDrains = new JButton("Dump drains")); dumpDrains.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { dumpDrains(); }}); JButton dumpEdges; north.add(dumpEdges = new JButton("Dump edges")); dumpEdges.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { dumpEdges(); }}); JButton dumpCores; north.add(dumpCores = new JButton("Dump cores")); dumpCores.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { dumpCores(); }}); this.add(north, BorderLayout.NORTH); summary = new JSplitPane(); this.startTime=startTime; 91 JTable simParams = new JTable(); String [] columnNames = {"Parameter", "Value"}; Properties props = System.getProperties(); Enumeration keys = props.propertyNames(); String key=null; int val=0; int num=0; // Find number of infiniband properties while(keys.hasMoreElements()) { key = (String) keys.nextElement(); if (key.startsWith("infiniband.")) { num++; } } String [][] data = new String[num][2]; // Read properties keys = props.propertyNames(); for(int i=0; keys.hasMoreElements();) { key = (String) keys.nextElement(); if (key.startsWith("infiniband.")) { data[i][0] = key; try{ val = Integer.parseInt(props.getProperty(key)); data[i][1] = Utils.formatInt(val); }catch(NumberFormatException nfe){ data[i][1] = props.getProperty(key); } i++; } } simParams.setModel(paramsModel = new CustomTableModel(data, columnNames)); summary.setLeftComponent(new JScrollPane(simParams, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); // Stats about approximate object sizes long freeMem; long switchSize, linkSize, sourceNodeSize, drainNodeSize, packetSize; IBASwitch test; Link link; IBASourceNode s; IBADrainNode d; IBAPacket p; Kernel kernel = new Kernel(Integer.getInteger("infiniband.STOP_TIME", 0).intValue()); int numberOfVLs = 16; int inputBufferSize = 2 * Integer.parseInt( System.getProperty( "infiniband.EXP_PACKET_SIZE")); int outputBufferSize = 2 * Integer.parseInt( System.getProperty( "infiniband.EXP_PACKET_SIZE")); System.gc(); freeMem = Runtime.getRuntime().freeMemory(); test = new IBASwitch(4, 4, numberOfVLs, inputBufferSize, outputBufferSize, "TestSwitch", kernel); switchSize = freeMem-Runtime.getRuntime().freeMemory(); test = null; System.gc(); freeMem = Runtime.getRuntime().freeMemory(); link = new Link(kernel); linkSize = freeMem-Runtime.getRuntime().freeMemory(); link = null; System.gc(); freeMem = Runtime.getRuntime().freeMemory(); s = new IBASourceNode(1, numberOfVLs, "Test source", kernel); sourceNodeSize = freeMem-Runtime.getRuntime().freeMemory(); s = null; 92 System.gc(); freeMem = Runtime.getRuntime().freeMemory(); d = new IBADrainNode(1, numberOfVLs, "Test drain", kernel); drainNodeSize = freeMem-Runtime.getRuntime().freeMemory(); d = null; System.gc(); freeMem = Runtime.getRuntime().freeMemory(); p = new IBAPacket(); p.lrh.setLNH(IBAPacket.LNH_IBA_local); p.bth.setOpCode(IBAPacket.RD_SEND_ONLY); StringBuffer str = new StringBuffer(); int numChars = Integer.parseInt(System.getProperty("infiniband.EXP_PAYLOAD_SIZE")); for (int i=0; i < numChars; i++){ str.append((char) ((int) ’A’)); } p.pyld.setContent(str.toString().getBytes()); p.updatePacketLength(); packetSize = freeMem-Runtime.getRuntime().freeMemory(); p = null; kernel = null; System.gc(); // Counting/time stats JTable counter = new JTable(); columnNames = new String[2]; columnNames[0] = "Variable"; columnNames[1] = "Value"; String [][] statVars = { {"Time step", "0"}, threshold", {"Transient Utils.formatInt(Integer.getInteger("infiniband.TRANSIENT_PERIOD",0).intValue())}, {"Transient period finished", "NO"}, time used", "0"}, {"Instant speed", "0 time steps/second"}, {"Average speed", "0 time steps/second"}, {"Kernel’s item queue length", "0"}, {"Total allocated memory", Utils.formatLong(Runtime.getRuntime().totalMemory())+" B"}, {"Free memory of allocated", Utils.formatLong(Runtime.getRuntime().freeMemory())+" B"}, {"Maximum memory", Utils.formatLong(Runtime.getRuntime().maxMemory())+" B"}, {"Size of a switch", Utils.formatLong(switchSize)+" B"}, {"Size of a link", Utils.formatLong(linkSize)+" B"}, {"Size of a source node", Utils.formatLong(sourceNodeSize)+" B"}, {"Size of a drain node", Utils.formatLong(drainNodeSize)+" B"}, {"Size of a packet", Utils.formatLong(packetSize)+" B"} {"Real }; counter.setModel(counterModel = new CustomTableModel(statVars, columnNames)); summary.setRightComponent(new JScrollPane(counter, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); summary.setDividerLocation(400); tabs.addTab("Simulation summary", summary); sourceNodes = new JLabel[numSourceNodes][3]; drainNodes = new JLabel[numDrainNodes]; edgeSwitches = new JLabel[numEdgeSwitches][8]; coreSwitches = new JLabel[numCoreSwitches][8]; // Sources JTable sourcesTable = new JTable(); columnNames = new String[4]; columnNames[0] = columnNames[1] = "Node name"; "Packets sent"; 93 columnNames[2] = columnNames[3] = "Packets waiting"; "Effective usage"; data = new String[numSourceNodes][4]; for (int i=0; i < numSourceNodes; i++){ data[i][0] = data[i][1] = data[i][2] = data[i][3] = "Source "+addrPrefix+"."+i; "0%"; "0%"; "0%"; } sourcesTable.setModel(sourcesModel = new CustomTableModel(data, columnNames)); tabs.addTab("Source nodes", new JScrollPane(sourcesTable, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); // Drains JTable drainsTable = new JTable(); columnNames = new String[numSourceNodes + 1]; columnNames[0] = for "Node name"; (int i=0; i < numSourceNodes; i++) { columnNames[i+1] = "Source "+addrPrefix+"."+i; } data = new String[numDrainNodes][numSourceNodes+1]; for (int i=0; i < numDrainNodes; i++) { data[i][0] = for "Drain "+addrPrefix+"."+i; (int j = 1; j < data[i].length; j++) { data[i][j] = "0"; } } drainsTable.setModel(drainsModel = new CustomTableModel(data, columnNames)); tabs.addTab("Drain nodes", new JScrollPane(drainsTable, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); /* // Fix width of cells in the table TableColumn column = null; Component comp = null; int headerWidth = 0; int cellWidth = 0; for (int i = 0; i < data[0].length; i++) { column = drainsTable.getColumnModel().getColumn(i); comp = drainsTable.getTableHeader().getDefaultRenderer(). getTableCellRendererComponent( null, column.getHeaderValue(), false, false, 0, 0); headerWidth = comp.getPreferredSize().width; comp = drainsTable.getDefaultRenderer(drainsModel.getColumnClass(i)). getTableCellRendererComponent( drainsTable, ""+data[0][i], false, false, 0, i); cellWidth = comp.getPreferredSize().width; column.setPreferredWidth(Math.max(headerWidth, cellWidth)); 94 } */ // Edge switches JTable edgesTable = new JTable(); String[] switchHeading = {"Switch Name", Port 0", Utilization", Port 1", Utilization", Port 2", Utilization", Port 3", Utilization"}; "Packets "Port 0, "Packets "Port 1, "Packets "Port 2, "Packets "Port 3, data = new String[numEdgeSwitches][switchHeading.length]; for (int i=0; i < numEdgeSwitches; i++) { data[i][0] = for "Edge Switch "+i; (int j=1; j < data[i].length; j+=2) { data[i][j] = "0"; "0%"; data[i][j+1] = } } edgesTable.setModel(edgesModel = new CustomTableModel(data, switchHeading)); tabs.addTab("Edge switches", new JScrollPane(edgesTable, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); // Core switches JTable coresTable = new JTable(); data = new String[numCoreSwitches][switchHeading.length]; for (int i=0; i < numCoreSwitches; i++) { data[i][0] = for "Core Switch "+i; (int j=1; j < data[i].length; j+=2) { data[i][j] = "0"; "0%"; data[i][j+1] = } } coresTable.setModel(coresModel = new CustomTableModel(data, switchHeading)); tabs.addTab("Core switches", new JScrollPane(coresTable, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); } public void setSourceStats(int sourceNo, int sent, int waiting){ sourcesModel.setValueAt(""+sent, sourceNo, 1); sourcesModel.setValueAt(""+waiting,sourceNo, 2 ); if (sent > 0) { int usagePercentage = Integer.getInteger("infiniband.USAGE_PERCENTAGE", 100).intValue(); sourcesModel.setValueAt(""+Utils.formatDouble(((double)sent)/(sent+waiting)* usagePercentage)+"%", sourceNo, 3); } } 95 public void setDrainStats(int drainNo, no.uio.ifi.gruns.infiniband.IBAStatistics.IBADrainNodeStats stats){ HashMap sources; Set keys; SortedSet sortedKeys; Iterator it; String key; sources = stats.getPacketsReceivedPerSource(); int col = 0; keys = sources.keySet(); sortedKeys = new TreeSet(new IBAUnitComparator()); sortedKeys.addAll(keys); it = sortedKeys.iterator(); while(it.hasNext()) { key = (String) it.next(); //System.err.println("colOfKey(Source "+key+") = "+drainsModel.colOfKey("Source "+key)); col = drainsModel.colOfKey("Source "+key); drainsModel.setValueAt(""+sources.get(key), drainNo, col); } } /** * Calculates the percentage of utilization of a unit in the network. * Divides the total number of bytes sent with the theoretical * maximum which is the number of time steps elapsed, <b>t</b>, times * the number of bytes per block, <b>n</b>. Since the simulator * can process one block per time step, this should give a good percentage * estimation. */ private double usagePercentage(int bytesSent) { return 100 * ((double) (bytesSent * Integer.getInteger("infiniband.REAL_PACKET_SIZE").intValue())) / ((currentTimeStep-Integer.getInteger("infiniband.TRANSIENT_PERIOD", 0).intValue()) Integer.getInteger("infiniband.BLOCK_SIZE").intValue()); * } public void setEdgeStats(int edgeNo, int[] sent) { for (int i=0; i < sent.length; i++) { edgesModel.setValueAt(""+sent[i], edgeNo, (i*2)+1); // Percent edgesModel.setValueAt(""+Utils.formatDouble(usagePercentage(sent[i]))+"%", edgeNo, (i*2)+2); } } public void setCoreStats(int coreNo, int[] sent) { for (int i=0; i < sent.length; i++) { coresModel.setValueAt(""+sent[i], coreNo, (i*2)+1); // Percent coresModel.setValueAt(""+Utils.formatDouble(usagePercentage(sent[i]))+"%", coreNo, (i*2)+2); } } public void setTime(int time) { /* String [][] statVars = { {"Time step", "0"}, {"Real time used", "0"}, {"Instant speed", "0 time steps/second"}, {"Average speed", "0 time steps/second"}, {"Kernel’s item queue length", "0"}, {"Total allocated memory", Utils.formatLong(Runtime.getRuntime().totalMemory())+" KB"}, {"Free memory of allocated", Utils.formatLong(Runtime.getRuntime().freeMemory())+" KB"}, {"Maximum memory", Utils.formatLong(Runtime.getRuntime().maxMemory())+" KB"} }; */ 96 currentTimeStep = time; long currTime = System.currentTimeMillis(); int rowNum; rowNum = counterModel.rowOfKey("Time step"); counterModel.setValueAt(Utils.formatInt(time)+" of "+ Utils.formatInt(Integer.parseInt( System.getProperty("infiniband.STOP_TIME"))), rowNum, 1); rowNum = counterModel.rowOfKey("Real time used"); counterModel.setValueAt(Utils.timeFormat(currTime - startTime), rowNum, 1); long speed = (1000 * (lastTimeStep - time)) / (lastRealTime - currTime); long avgSpeed = (1000 * time) / (currTime-startTime); rowNum = counterModel.rowOfKey("Instant speed"); " time steps/second", counterModel.setValueAt(Utils.formatLong(speed)+ rowNum = counterModel.rowOfKey("Average rowNum, 1); speed"); " time steps/second", counterModel.setValueAt(Utils.formatLong(avgSpeed)+ rowNum = counterModel.rowOfKey("Transient rowNum, 1); period finished"); counterModel.setValueAt((time > Integer.getInteger("infiniband.TRANSIENT_PERIOD", 0).intValue()) ?"YES" :"NO", rowNum, 1); lastTimeStep = time; lastRealTime = currTime; } public void setQueueLen(int length) { int rowNum = counterModel.rowOfKey("Kernel’s item queue length"); counterModel.setValueAt(Utils.formatInt(length), rowNum, 1); } public void updateMemUsage() { int rowNum; rowNum = counterModel.rowOfKey("Total allocated memory"); counterModel.setValueAt(Utils.formatLong(Runtime.getRuntime().totalMemory())+" rowNum, 1); rowNum = counterModel.rowOfKey("Free memory of allocated"); counterModel.setValueAt(Utils.formatLong(Runtime.getRuntime().freeMemory())+" rowNum, 1); rowNum = counterModel.rowOfKey("Maximum B", memory"); counterModel.setValueAt(Utils.formatLong(Runtime.getRuntime().maxMemory())+" rowNum, 1); } public String getData() { StringBuffer retVal = new StringBuffer(); retVal.append(paramsModel); retVal.append("\n\n"); retVal.append(counterModel); retVal.append("\n\n"); retVal.append(sourcesModel); retVal.append("\n\n"); retVal.append(drainsModel); retVal.append("\n\n"); retVal.append(edgesModel); retVal.append("\n\n"); retVal.append(coresModel); return B", retVal.toString(); } 97 B", public void dumpData() { Reporter.println(getData()); } public void dumpSources() { Reporter.println(sourcesModel); } public void dumpDrains() { Reporter.println(drainsModel); } public void dumpEdges() { Reporter.println(edgesModel); } public void dumpCores() { Reporter.println(coresModel); } public void dumpParams() { Reporter.println(paramsModel); } public void dumpCounter() { Reporter.println(counterModel); } public static void main(String [] args) { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); StatusPanel panel = new StatusPanel(12,12,12,16,System.currentTimeMillis()); f.setContentPane(panel); f.setSize(500,500); f.setVisible(true); } } 98 no.uio.ifi.gruns.infiniband.IBAConstants package no.uio.ifi.gruns.infiniband; /** * Defines some constants needed in some of the IBA classes. */ public final class IBAConstants{ /** * Indicates for how long to wait for the network to * enter a steady state. Then we can start collecting data. */ public static final int IBA_TRANSIENT_PERIOD = 10000; /** * The size of an IBA block in bytes. * One block is processed on a given time step in the simulator. */ public static final int IBA_BLOCK_SIZE = 64; /** * The number of time steps it takes to process an IBA * packet in a switch (i.e. to look up the address) */ public static final int IBA_PACKET_PROCESSING_TIME = 5; /** * The time to wait before rescheduling * in a switch if the output VL is not free. */ public static final int IBA_SLEEP_TIME = 1; /** * The maximum number of virtual lanes on a link */ public static final int MAX_NUMBER_OF_VLS = 16; /** * The overhead of the packets used in the * experiments */ public static final int EXP_OVERHEAD = 38; /** * The packet size used in experiments */ public static final int EXP_PACKET_SIZE = 4096; //public static final int EXP_PACKET_SIZE = 1024; /** * The size of the payload used in the * experiments */ public static final int EXP_PAYLOAD_SIZE = EXP_PACKET_SIZE - EXP_OVERHEAD; // the one above used to test priorities. The limit of high priority // states how many chunks of 4K bytes can be sent on hi pri before // we get to send a packet on low pri. /** * Loads all the constants as system properties. */ 99 public static final void loadProperties(){ if (System.getProperty("infiniband.TRANSIENT_PERIOD") System.setProperty("infiniband.TRANSIENT_PERIOD", == null) { Integer.toString(IBA_TRANSIENT_PERIOD)); } if (System.getProperty("infiniband.BLOCK_SIZE") == null){ System.setProperty("infiniband.BLOCK_SIZE", Integer.toString(IBA_BLOCK_SIZE)); } if (System.getProperty("infiniband.PACKET_PROCESSING_TIME") == null){ System.setProperty("infiniband.PACKET_PROCESSING_TIME", Integer.toString(IBA_PACKET_PROCESSING_TIME)); } if (System.getProperty("infiniband.SLEEP_TIME") == null){ System.setProperty("infiniband.SLEEP_TIME", Integer.toString(IBA_SLEEP_TIME)); } if (System.getProperty("infiniband.MAX_NUMBER_OF_VLS") == null){ System.setProperty("infiniband.MAX_NUMBER_OF_VLS", Integer.toString(MAX_NUMBER_OF_VLS)); } if (System.getProperty("infiniband.EXP_OVERHEAD") == null){ System.setProperty("infiniband.EXP_OVERHEAD", Integer.toString(EXP_OVERHEAD)); } if (System.getProperty("infiniband.EXP_PACKET_SIZE") == null){ System.setProperty("infiniband.EXP_PACKET_SIZE", Integer.toString(EXP_PACKET_SIZE)); } if (System.getProperty("infiniband.EXP_PAYLOAD_SIZE") == null){ int pyld_size = Integer.parseInt( System.getProperty("infiniband.EXP_PACKET_SIZE")) Integer.parseInt(System.getProperty("infiniband.EXP_OVERHEAD")); System.setProperty("infiniband.EXP_PAYLOAD_SIZE", Integer.toString(pyld_size)); } int realPktSize = Integer.parseInt(System.getProperty("infiniband.EXP_PACKET_SIZE")) + 2; System.setProperty("infiniband.REAL_PACKET_SIZE", } } 100 ""+(realPktSize)); /* VCRC */ no.uio.ifi.gruns.infiniband.IBADelayedSwitch package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; /** * Introduces a delay in the sending of packets in the output port * by trying to figure out if there’s traffic coming on the high priority lanes * in the output ports, and waiting until this data is available before * sending from the output port. The goal is to avoid sending a packet on low * priority just because the high priority packets are delayed in the switch. * * However, this doesn’t work very well, because it is impossible to know anything * about the next packet on a particular VL when the switch has not yet read this packet * since it hasn’t arrived in the switch yet. */ public class IBADelayedSwitch extends IBASwitch { // {{{ public IBADelayedSwitch(...) /** * @param numberOfInputPorts The number of input ports * @param numberOfOutputPorts The number of output ports * @param inputBufferSize The size of the buffers in the input * ports. Affects how the switch acts, * because when the output buffers go full, the low * priority queues get more priority than when * there’s still plenty of space. * @param outputBufferSize The size of the buffers in the output * ports. * @param numberOfVirtualLanes The number of virtual lanes on * input and output ports. * @param ID A string identifying the switch * @param kernel The kernel to use for simulation */ public IBADelayedSwitch(int numberOfInputPorts, int numberOfOutputPorts, int numberOfVirtualLanes, int inputBufferSize, int outputBufferSize, String ID, Kernel kernel){ super(kernel); int i; // {{{ DEBUG Reporter.debugln("IBADelayedSwitch"); // }}} this.inputBufferSize = inputBufferSize; this.outputBufferSize = outputBufferSize; // Create input and output ports inputPorts = new IBAInputPort[numberOfInputPorts]; for (i=0; i < numberOfInputPorts; i++){ inputPorts[i] = new IBAInputPort(numberOfVirtualLanes, inputBufferSize, kernel); inputPorts[i].setSink(this); inputPorts[i].setID(ID+", input port "+i); } outputPorts = new IBADelayedOutputPort[numberOfOutputPorts]; 101 for (i=0; i < numberOfOutputPorts; i++){ outputPorts[i] = new IBADelayedOutputPort(numberOfVirtualLanes, outputBufferSize, kernel,this,i); outputPorts[i].setID(ID+", delayed output port "+i); } // Assign ID this.ID = ID+ " (delayed) "; // Other initialization VLstates = new VLstate[inputPorts.length][numberOfVirtualLanes]; for (i=0; i < VLstates.length; i++){ for (int j=0; j < VLstates[i].length; j++){ VLstates[i][j] = new VLstate(); } } } // }}} /** * Returns all the states that have the output port * of the specified output port number. * TODO: This is impossible, because the packets have not yet been * processed, we don’t know if there are coming any packets to this * particular output port. * */ VLstate[][] getStates(){ // {{{ Kommentert ut /* Reporter.println("IBADelayedSwtich.getStates: VLstates.length = "+VLstates.length); java.util.Vector states = new java.util.Vector(VLstates.length); VLstate st = null; for (int i=0; i < VLstates.length; i++){ for (int j=0; j < VLstates[i].length; j++){ if (st != null){ Reporter.println("st = "+st); if (st.outputPort != null){ Reporter.println("st.outputPort.portNumber = "+ st.outputPort.portNumber+", "+ "st.outputPort.VLNumber = "+ st.outputPort.VLNumber); } } st = VLstates[i][j]; if (st != null && st.outputPort!=null && st.outputPort.portNumber == portNumber){ states.add(st); } } } VLstate[] retVal = new VLstate[states.size()]; retVal = (VLstate[]) states.toArray(retVal); return retVal; */ // }}} 102 return VLstates; } } 103 no.uio.ifi.gruns.infiniband.IBADelayedSwitch2 package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; /** * Introduces a delay between each packet in the output port equal to the * time the switch uses to process a packet. * The thought is to avoid sending packets on low priority when there are packets * on their way on high priority. * * The problem is of course that we introduce a delay on all packets, even if there * aren’t coming any more packets on high priority. */ public class IBADelayedSwitch2 extends IBASwitch { // {{{ public IBADelayedSwitch2(...) /** * @param numberOfInputPorts The number of input ports * @param numberOfOutputPorts The number of output ports * @param inputBufferSize The size of the buffers in the input * ports. Affects how the switch acts, * because when the output buffers go full, the low * priority queues get more priority than when * there’s still plenty of space. * @param outputBufferSize The size of the buffers in the output * ports. * @param numberOfVirtualLanes The number of virtual lanes on * input and output ports. * @param ID A string identifying the switch * @param kernel The kernel to use for simulation */ public IBADelayedSwitch2(int numberOfInputPorts, int numberOfOutputPorts, int numberOfVirtualLanes, int inputBufferSize, int outputBufferSize, String ID, Kernel kernel){ super(kernel); int i; // {{{ DEBUG Reporter.debugln("IBADelayedSwitch"); // }}} this.inputBufferSize = inputBufferSize; this.outputBufferSize = outputBufferSize; // Create input and output ports inputPorts = new IBAInputPort[numberOfInputPorts]; for (i=0; i < numberOfInputPorts; i++){ inputPorts[i] = new IBAInputPort(numberOfVirtualLanes, inputBufferSize, kernel); inputPorts[i].setSink(this); inputPorts[i].setID(ID+", input port "+i); } outputPorts = new IBADelayedOutputPort2[numberOfOutputPorts]; for (i=0; i < numberOfOutputPorts; i++){ outputPorts[i] = new IBADelayedOutputPort2(numberOfVirtualLanes, outputBufferSize, 104 kernel,this,i); outputPorts[i].setID(ID+", delayed output port "+i); } // Assign ID this.ID = ID+ " (delayed) "; // Other initialization VLstates = new VLstate[inputPorts.length][numberOfVirtualLanes]; for (i=0; i < VLstates.length; i++){ for (int j=0; j < VLstates[i].length; j++){ VLstates[i][j] = new VLstate(); } } } // }}} /** * Returns all the states that have the output port * of the specified output port number. * TODO: This is impossible, because the packets have not yet been * processed, we don’t know if there are coming any packets to this * particular output port. * */ VLstate[][] getStates(){ // {{{ Kommentert ut /* Reporter.println("IBADelayedSwtich.getStates: VLstates.length = "+VLstates.length); java.util.Vector states = new java.util.Vector(VLstates.length); VLstate st = null; for (int i=0; i < VLstates.length; i++){ for (int j=0; j < VLstates[i].length; j++){ if (st != null){ Reporter.println("st = "+st); if (st.outputPort != null){ Reporter.println("st.outputPort.portNumber = "+ st.outputPort.portNumber+", "+ "st.outputPort.VLNumber = "+ st.outputPort.VLNumber); } } st = VLstates[i][j]; if (st != null && st.outputPort!=null && st.outputPort.portNumber == portNumber){ states.add(st); } } } VLstate[] retVal = new VLstate[states.size()]; retVal = (VLstate[]) states.toArray(retVal); return retVal; */ // }}} return VLstates; } } 105 no.uio.ifi.gruns.infiniband.IBAPacket package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; /** * Represents * The packet * * <pre> * * Bit number * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * an InfiniBand Architecture (IBA) Packet. format is the following: 0 4 8 12 16 20 24 28 32 ----------------------------------------------------------------|VL |LVer |SL |r |L | Destination | | | | |s |N | Local | | | | |v |H | ID | ----------------------------------------------------------------| r | | Source | | s | PktLen | Local | | v | | ID | --------------------------------------------------------------------------------------------------------------------------------|IP |TClass | Flow Label | |ver | | 20 bits | ----------------------------------------------------------------|Payload Length |NxtHdr |HopLmt | | 16 bits | 8b | 8b | ----------------------------------------------------------------| Source GID 127-96 | ----------------------------------------------------------------| Source GID 95-64 | ----------------------------------------------------------------| Source GID 63-32 | ----------------------------------------------------------------| Source GID 31-0 | ----------------------------------------------------------------| Destination GID 127-96 | ----------------------------------------------------------------| Destination GID 95-64 | ----------------------------------------------------------------| Destination GID 63-32 | ----------------------------------------------------------------| Destination GID 31-0 | --------------------------------------------------------------------------------------------------------------------------------| OpCode |S|M|Pad| TVer | Partition Key | | |E| |Cnt| | | ----------------------------------------------------------------| Reserved | Destination Queue Pair (QP) Number | | (variant) | | ----------------------------------------------------------------|A| Reserved | Packet Sequence Number (PSN) | --------------------------------------------------------------------------------------------------------------------------------| resv | EE-Context | ----------------------------------------------------------------- 106 Local Routing Header - LRH 8 bytes Present in all packets of a message Global Routing Header - GRH 40 bytes Present in all packets of a message, if indicate by Link Next Header in LRH Base Transport Header - BTH 12 bytes Present in all packets of a message if indicated by LNH, i.e. not a raw packe Reliable Datagram Extended Transport Header - RDETH * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 4 bytes ----------------------------------------------------------------| Queue Key | ----------------------------------------------------------------| resv 8b | Source QP (SrcQP) 24b | ----------------------------------------------------------------- Datagram Extended Transport Header - D 8 bytes Present in every pac of datagram request messages ----------------------------------------------------------------| Virtual Address 63-32 | ----------------------------------------------------------------| Virtual Address 31-0 | ----------------------------------------------------------------| Remote Key | ----------------------------------------------------------------| DMA Length | --------------------------------------------------------------------------------------------------------------------------------| Virtual Address 63-32 | ----------------------------------------------------------------| Virtual Address 31-0 | ----------------------------------------------------------------| Remote Key | ----------------------------------------------------------------| Swap (or Add) Data 63-32 | ----------------------------------------------------------------| Swap (or Add) Data 31-0 | ----------------------------------------------------------------| Compare Data 63-32 | ----------------------------------------------------------------| Compare Data 31-0 | --------------------------------------------------------------------------------------------------------------------------------| Syndrome | Message Sequence Number | ----------------------------------------------------------------- RDMA Extended Transport Header - R 16 bytes Present in first pac of RDMA request mess Atomic Extended Transport Header - A 28 bytes Present in Atomic request message Ack extended Transpo Header - AETH 4 bytes Present in all ACK p ----------------------------------------------------------------| Original Remote Data 63-32 | ----------------------------------------------------------------| Original Remote data 31-0 | ----------------------------------------------------------------- Atomic ACK Extended Transport Header AtomicAckETH - 8 byt Present in all Atomi packets ----------------------------------------------------------------| Immediate Data | ----------------------------------------------------------------- Immeditate Data - 4 Present in last pack of requests with immediate data ----------------------------------------------------------------| Payload | | | 107 Payload 0-4096 bytes * | | * | | * | ------------------------------------------------| * | | Pad 0-3 bytes | * ----------------------------------------------------------------* * ----------------------------------------------------------------* | ICRC | * ----------------------------------------------------------------* * * * * * * --------------------------------* | VCRC | * --------------------------------* * * * */ public class IBAPacket extends Packet{ /** The number of bits in a byte (8) */ public static final int BITS_PER_BYTE = 8; // Size of the different header fields /** The size of the Local Route Header */ public static final int LRH_SIZE = 8 * BITS_PER_BYTE; /** The size of the Global Route Header */ public static final int GRH_SIZE = 40 * BITS_PER_BYTE; /** The size of the Base Transport Header */ public static final int BTH_SIZE = 12 * BITS_PER_BYTE; /** The size of the Reliable Datagram Extended Transport Header */ public static final int RDETH_SIZE = 4 * BITS_PER_BYTE; /** The size of the Datagram Extended Transport Header */ public static final int DETH_SIZE = 8 * BITS_PER_BYTE; /** The size of the RDMA Extended Transport Header */ public static final int RETH_SIZE = 16 * BITS_PER_BYTE; /** The size of the Atomic Extended Transport Header */ public static final int ATOMIC_ETH_SIZE = 28 * BITS_PER_BYTE; /** The size of the ACK Extended Transport Header */ public static final int AETH_SIZE = 4 * BITS_PER_BYTE; /** The size of the Atomic ACK Extended Transport Header */ public static final int ATOMIC_ACK_ETH_SIZE = 8 * BITS_PER_BYTE; /** The size of the Immediate Data Extended Transport Header */ public static final int IMM_DT_SIZE = 4 * BITS_PER_BYTE; = 4 * BITS_PER_BYTE; = 2 * BITS_PER_BYTE; /** The size of the ICRC */ public static final int ICRC_SIZE /** The size of the VCRC */ public static final int VCRC_SIZE /** 108 Invariant CRC 4 bytes Present in all packe of message, if indic by Link Next Header (i.e. not a raw pack Variant CRC 2 bytes Present in all packe of message. * The * The * But * the * See */ maximum size of an IBA packet in bytes. actual max value in IBA is 4096 + 126. we don’t have any fields befor the LRH, and only 2 bytes after Variant CRC, so we don’t add 126 bytes, only 2. section 7.7.8 in the IBA spec. public static final int MAX_SIZE = (4096 + 2) ; // Needed for the LRH /** Indicates that this is a global packet */ public static byte LNH_IBA_global = (byte) 0x03; /** Indicates that this is a local packet */ public static byte LNH_IBA_local = (byte) 0x02; /** Indicates that this is a non-IBA packet */ public static byte LNH_IP_non_IBA = (byte) 0x01; /** Indicates that this is a raw packet */ public static byte LNH_raw = (byte) 0x00; // Needed for the OpCode field in the BTH. Types of packets. public static byte RD_TYPE = 0x40; /** * Indicates that the type of the packet is * Reliable Datagram, SEND First. */ public static byte RD_SEND_FIRST = (byte) (RD_TYPE | 0x00); /** *Indicates that the type of the packet is * Reliable Datagram, SEND Middle. */ public static byte RD_SEND_MIDDLE = (byte) (RD_TYPE | 0x01); /** * Indicates that the type of the packet is * Reliable Datagram, SEND Last. */ public static byte RD_SEND_LAST = (byte) (RD_TYPE | 0x02); /** * Indicates that the type of the packet is * Reliable Datagram, SEND Last with Immediate. */ public static byte RD_SEND_LAST_WITH_IMMEDIATE = (byte) (RD_TYPE | 0x03); /** * Indicates that the type of the packet is * Reliable Datagram, SEND Only. */ public static byte RD_SEND_ONLY = (byte) (RD_TYPE | 0x04); /** * Indicates that the type of the packet is * Reliable Datagram, SEND Only with Immediate. */ public static byte RD_SEND_ONLY_WITH_IMMEDIATE = (byte) (RD_TYPE | 0x05); /** * Indicates that the type of the packet is * Reliable Datagram, RDMA WRITE First. */ public static byte RD_RDMA_WRITE_FIRST = (byte) (RD_TYPE | 0x06); 109 /** * Indicates that the type of the packet is * Reliable Datagram, RDMA WRITE Middle. */ public static byte RD_RDMA_WRITE_MIDDLE = (byte) (RD_TYPE | 0x07); /** * Indicates that the type of the packet is * Reliable Datagram, RDMA WRITE Last. */ public static byte RD_RDMA_WRITE_LAST = (byte)( RD_TYPE | 0x08); /** * Indicates that the type of the packet is * Reliable Datagram, RDMA WRITE Last with Immediate. */ public static byte RD_RDMA_WRITE_LAST_WITH_IMMEDIATE = (byte) (RD_TYPE | 0x09); /** * Indicates that the type of the packet is * Reliable Datagram, RDMA WRITE Only. */ public static byte RD_RDMA_WRITE_ONLY = (byte) (RD_TYPE | 0x0A); /** * Indicates that the type of the packet is * Reliable Datagram, RDMA WRITE Only with Immediate. */ public static byte RD_RDMA_WRITE_ONLY_WITH_IMMEDIATE = (byte) (RD_TYPE | 0x0B); /** * Indicates that the type of the packet is * Reliable Datagram, RDMA READ Request. */ public static byte RD_RDMA_READ_REQUEST = (byte) (RD_TYPE | 0x0C); /** * Indicates that the type of the packet is * Reliable Datagram, RDMA READ response First. */ public static byte RD_RDMA_READ_RESPONSE_FIRST = (byte) (RD_TYPE | 0x0D); /** * Indicates that the type of the packet is * Reliable Datagram, RDMA READ response Middle. */ public static byte RD_RDMA_READ_RESPONSE_MIDDLE = (byte) (RD_TYPE | 0x0E); /** * Indicates that the type of the packet is * Reliable Datagram, RDMA READ response Last. */ public static byte RD_RDMA_READ_RESPONSE_LAST = (byte) (RD_TYPE | 0x0F); /** * Indicates that the type of the packet is * Reliable Datagram, RDMA READ response Only. */ public static byte RD_RDMA_READ_RESPONSE_ONLY = (byte) (RD_TYPE | 0x10); /** * Indicates that the type of the packet is 110 * Reliable Datagram, Acknowledge. */ public static byte RD_ACKNOWLEDGE = (byte) (RD_TYPE | 0x11); /** * Indicates that the type of the packet is * Reliable Datagram, ATOMIC acknowledge. */ public static byte RD_ATOMIC_ACKNOWLEDGE = (byte) (RD_TYPE | 0x12); /** * Indicates that the type of the packet is * Reliable Datagram, CmpSwap. */ public static byte RD_CMPSWAP = (byte) (RD_TYPE | 0x13); /** * Indicates that the type of the packet is * Reliable Datagram, FetchAdd. */ public static byte RD_FETCHADD = (byte) (RD_TYPE | 0x14); /** * Indicates that the type of the packet is * Reliable Datagram, RESYNC. */ public static byte RD_RESYNC = (byte) (RD_TYPE | 0x15); // Header types /** * <b>The Local Route Header</b> - 8 bytes / 64 bits<br> * *<p> * The Local Routing Header (LRH) contains fields used for local routing by * switches within the IBA subnet. *</p> * * Header format: *<pre> * * Bit number * 0 4 8 12 16 20 24 28 32 * ----------------------------------------------------------------* |VL |LVer |SL |r |L | Destination | * 0-31 | | | |s 1|N | Local | * | | | |v |H | ID | * ----------------------------------------------------------------* | r | | Source | * 32-63 | s 2 | PktLen | Local | * | v | | ID | * ----------------------------------------------------------------* *</pre> * */ public class LRH{ /** Size of the header in bits */ protected int size = LRH_SIZE; 111 /** The actual bytes that form the header */ protected byte[] content; /** * <b>Virtual lane</b> - 4 bits <br> * This field identifies the virtual lane that the packet * is using. */ public int VL(){ return ((content[0] & VL_MASK) >> 4) & 0x0F; } /** * <b>Virtual lane</b> - 4 bits <br> * Sets the field that identifies the virtual lane that the packet * is using. */ public void setVL(int VL){ // Throw an error message if the supplied value // is out of range if (VL > 15 || VL < 0){ throw new IllegalArgumentException("VL number out "+ "of range! "+ VL+" is not in "+ "the range (0-15)."); } content[0] = clear(content[0], VL_MASK); content[0] |= (byte) ((VL << 4) & VL_MASK); // Sanity check if (VL() != VL){ Reporter.error("ERROR setting VL!!!"); } // } /** * <b>Link Version</b> - 4 bits <br> * This field identifies the Link level protocol of this packet. * This version applies to the general packet * structure including the LRH fields and the variant CRC. */ public int LVer(){ int LVer = (content[0] & LVer_MASK) & 0x0F; return LVer; } /** * <b>Link Version</b> - 4 bits <br> * Sets the field that identifies the Link level protocol of * this packet. * This version applies to the general packet * structure including the LRH fields and the variant CRC. */ public void setLVer(int LVer){ // Throw an error message if the supplied value // is out of range if (LVer > 15 || LVer < 0) { throw new IllegalArgumentException("LVer out of range! "+ LVer+" is not in "+ "the range (0-15)."); } content[0] = clear(content[0], LVer_MASK); 112 content[0] |= (byte) (LVer & LVer_MASK); } /** * <b>Service Level</b> - 4 bits <br> * This field indicates what service level the packet * is requesting within the subnet. */ public int SL(){ int SL = ((content[1] & SL_MASK) >> 4) & 0x0F; return SL; } /** * <b>Service Level</b> - 4 bits <br> * Sets the field that indicates what service level the packet * is requesting within the subnet. */ public void setSL(int SL){ // Throw an error message if the supplied value // is out of range if (SL > 15 || SL < 0) { throw new IllegalArgumentException("SL value out "+ "of range! "+ SL+" is not in the "+ "range (0-15)."); } content[1] = clear(content[1], SL_MASK); content[1] |= ( ((byte) (SL << 4)) & SL_MASK); } /** * <b>Link Next Header</b> - 2 bits <br> * This field identifies the headers that follow the LRH. * The bit values are in the last two bits of the byte returned. */ public byte LNH(){ return (byte) (content[1] & LNH_MASK); } /** * <b>Link Next Header</b> - 2 bits <br> * Sets the field that identifies the headers that follow the LRH. * The bit values should be in the last two bits of the byte. */ public void setLNH(byte LNH){ content[1] = clear(content[1], LNH_MASK); content[1] |= (byte) (LNH & LNH_MASK); } /** * <b>Destination Local ID</b> - 16 bits <br> * This field identifies the destination port and path (data sink) * on the local subnet. */ public byte[] DLID(){ byte[] DLID = { content[2], content[3] }; return DLID; } /** * <b>Destination Local ID</b> - 16 bits <br> * Sets the field that identifies the destination * port and path (data sink) on the local subnet. */ 113 public void setDLID(byte[] DLID){ // Throw an error message if the supplied array // is of the wrong length. if (DLID.length != 2){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ DLID.length +"!=2"); } content[2] = DLID[0]; content[3] = DLID[1]; } /** * <b>Packet Length</b> - 11 bits <br> * This field identifies the size of the Packet in four-byte * words. * This field includes the first byte of LRH to the last byte * before the variant CRC. See the IBA specification, section * <i>7.7.8 Packet Length (PktLen) - 11 bits</i> on page 167 for * details on max and min values of PktLen. * The bit values are in the last eleven bits of the bytes * returned. */ public byte[] PktLenByte(){ byte [] PktLen = {(byte) (content[4] & PktLen_MASK[0]), (byte) (content[5] & PktLen_MASK[1]) }; return PktLen; } /** * <b>Packet Length</b> - 11 bits <br> * This field identifies the size of the Packet in four-byte * words. * This field includes the first byte of LRH to the last byte * before the variant CRC. See the IBA specification, section * <i>7.7.8 Packet Length (PktLen) - 11 bits</i> on page 167 for * details on max and min values of PktLen. */ public int PktLen(){ // Update the packet length in the lrh updatePacketLength(); int firstByte = (PktLenByte()[0] & PktLen_MASK[0]) << 8; int secondByte = (PktLenByte()[1] & PktLen_MASK[1]) & 0xFF; int pktLen = firstByte | secondByte; return pktLen; } /** * <b>Packet Length</b> - 11 bits <br> * Sets the field that identifies the size of the Packet in * four-byte words. * This field includes the first byte of LRH to the last byte * before the variant CRC. See the IBA specification, section * <i>7.7.8 Packet Length (PktLen) - 11 bits</i> on page 167 for * details on max and min values of PktLen. * The bit values should be in the last eleven bits of the bytes. */ public void setPktLen(byte[] PktLen){ 114 // Throw an error message if the supplied array // is of the wrong length. if (PktLen.length != 2){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ PktLen.length+"!=2"); } // zero out previous values of the length // to avoid or’ing e.g. 00010000 with 1111, expecting 00001111, // but getting 00011111 content[4] = clear(content[4], PktLen_MASK[0]); content[5] = clear(content[5], PktLen_MASK[1]); // Fill in right values. content[4] |= (byte) (PktLen[0] & PktLen_MASK[0]); content[5] |= (byte) (PktLen[1] & PktLen_MASK[1]); } public void setPktLen(int length){ if (length > (MAX_SIZE-2) / 4 ) { throw new IllegalArgumentException("Length "+length+" * 4 is too large. "+ "Maximum is "+ (((MAX_SIZE-2) / 4)-1)+" * 4"); } byte tmp1,tmp2; tmp1 tmp1 tmp2 tmp2 = (byte) ((length & 0xFF00) >> 8); &= PktLen_MASK[0]; = (byte) (length & 0x00FF); &= PktLen_MASK[1]; // zero out previous values of the length // to avoid or’ing e.g. 00010000 with 1111, expecting 00001111, // but getting 00011111 content[4] = clear(content[4],PktLen_MASK[0]); content[5] = clear(content[5],PktLen_MASK[1]); // Fill in the right values content[4] |= tmp1; content[5] |= tmp2; } /** * <b>Source Local ID</b> - 16 bits <br> * This field identifies the source port (injection point) * on the local subnet. */ public byte[] SLID(){ byte[] SLID = { content[6], content[7] }; return SLID; } /** * <b>Source Local ID</b> - 16 bits <br> * Sets the field that identifies the source port * (injection point) on the local subnet. */ public void setSLID(byte[] SLID){ // Throw an error message if the supplied array // is of the wrong length. if (SLID.length != 2){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ SLID.length+"!=2"); 115 } content[6] = clear(content[6], SLID_MASK[0]); content[7] = clear(content[7], SLID_MASK[1]); content[6] |= (byte) (SLID[0] & SLID_MASK[0]); content[7] |= (byte) (SLID[1] & SLID_MASK[1]); } /** Used to mask the VL */ protected final byte VL_MASK = (byte) 0xF0; /** Used to mask the LVer */ protected final byte LVer_MASK = (byte) 0x0F; /** Used to mask the SL */ protected final byte SL_MASK = (byte) 0xF0; /** Used to mask the LNH */ protected final byte LNH_MASK = (byte) 0x03; /** Used to mask the DLID */ protected final byte[] DLID_MASK = { (byte) 0xFF, (byte) 0xFF }; /** Used to mask the PktLen */ protected final byte[] PktLen_MASK = { (byte) 0x07, (byte) 0xFF }; /** Used to mask the SLID */ protected final byte[] SLID_MASK = { (byte) 0xFF, (byte) 0xFF }; /** * Creates an empty LRH (all zeros) */ protected LRH(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } /** * Creates a new LRH from the supplied byte array. * @param content This array must be 8 bytes. */ protected LRH(byte [] content){ // Throw an error message if the supplied array // is of the wrong length. if (content.length != size/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ content.length+"!="+ size/BITS_PER_BYTE); } this.content = content; } /** * Indicates whether this part of the header is present * in this packet. */ protected boolean isPresent(){ return true; } } /** *<b>The Global Route Header</b> <br> * 116 *<p> * Global Route Header (GRH) contains fields for routing the packet between * subnets. THe presence of the GRH is indicated by the Link Next Header (LNH) * field in the LRH. The layout of the GRH is the same as the IPv6 Header * defined in RFC 2640. Note, however, that IBA does not define a relationship * between a device GID and IPv6 address (i.e. There is no defined mapping between * GID and IPv6 address for any IB device of port). *</p> * * Header format: * *<pre> * 0 4 8 12 16 20 24 28 32 * ----------------------------------------------------------------* |IP |TClass | Flow Label | * |ver | | 20 bits | * ----------------------------------------------------------------* |Payload Length |NxtHdr |HopLmt | * | 16 bits | 8b | 8b | * ----------------------------------------------------------------* | Source GID 127-96 | * ----------------------------------------------------------------* | Source GID 95-64 | * ----------------------------------------------------------------* | Source GID 63-32 | * ----------------------------------------------------------------* | Source GID 31-0 | * ----------------------------------------------------------------* | Destination GID 127-96 | * ----------------------------------------------------------------* | Destination GID 95-64 | * ----------------------------------------------------------------* | Destination GID 63-32 | * ----------------------------------------------------------------* | Destination GID 31-0 | * ----------------------------------------------------------------*</pre> * */ public class GRH{ /** Size of the header in bits */ protected int size = GRH_SIZE; /** The actual bytes that form the header */ protected byte[] content; /** * <b>IP Version</b> - 4 bits <br> * This field indicates version of the GRH. * The bit values are in the first four bits of the * bytes returned. */ public byte IPVer(){ return (byte) (content[0] & IPVer_MASK); } /** * <b>IP Version</b> - 4 bits <br> * This field indicates version of the GRH. * The bit values should be in the first four bits of the bytes. */ public void setIPVer(byte IPVer){ 117 content[0] = clear(content[0], IPVer_MASK); content[0] |= (byte) (content[0] & IPVer_MASK); } /** * <b>Traffic Class</b> - 8 bits <br> * This field is used by IBA to communicate global service level. * The bit values are in the last four bits of the first byte * and the first four bits of the last byte returned. */ public byte[] TClass(){ byte [] TClass = { (byte) (content[0] & TClass_MASK[0]), (byte) (content[1] & TClass_MASK[1]) }; return TClass; } /** * <b>Traffic Class</b> - 8 bits <br> * This field is used by IBA to communicate global service level. * The bit values should be in the last four bits of * the first byte and the first four bits of the last byte. */ public void setTClass(byte[] TClass){ // Throw an error message if the supplied array // is of the wrong length. if (TClass.length != 2){ throw new IllegalArgumentException("Length of supplied "+ " array is "+ "incorrect! "+ TClass.length+"!=2"); } content[0] = clear(content[0], TClass_MASK[0]); content[1] = clear(content[1], TClass_MASK[1]); content[0] |= (byte) (TClass[0] & TClass_MASK[0]); content[1] |= (byte) (TClass[1] & TClass_MASK[1]); } /** * <b>Flow Label</b> - 20 bits <br> * This field identifies sequences of packets requiring * special handling. * The bit values are in the last four bits of the first byte * and inn all of the last two bytes that are returned. */ public byte[] FlowLabel(){ byte[] FlowLabel = { (byte) (content[1] & FlowLabel_MASK[0]), (byte) (content[2] & FlowLabel_MASK[1]), (byte) (content[3] & FlowLabel_MASK[2])}; return FlowLabel; } /** * <b>Flow Label</b> - 20 bits <br> * This field identifies sequences of packets requiring special * handling. * The bit values should be in the last four bits * of the first byte and all the bits of the last two bytes. */ public void setFlowLabel(byte[] FlowLabel){ // Throw an error message if the supplied array // is of the wrong length. if (FlowLabel.length != 3){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ 118 FlowLabel.length+ "!=3"); } content[1] = clear(content[1], FlowLabel_MASK[0]); content[2] = clear(content[2], FlowLabel_MASK[1]); content[3] = clear(content[3], FlowLabel_MASK[2]); content[1] |= (byte) (FlowLabel[0] & FlowLabel_MASK[0]); content[2] |= (byte) (FlowLabel[1] & FlowLabel_MASK[1]); content[3] |= (byte) (FlowLabel[2] & FlowLabel_MASK[2]); } /** * <b>Payload length</b> - 16 bits <br> * For an IBA packet this field specifies the number of * bytes starting from the first byte after the GRH, up to and * including the last byte of the ICRC. For a raw IPv6 datagram * this field specifies the number of bytes starting from the * first byte after the GRH, up to but not including either * the VCRC or any padding, to achieve a multiple of 4 byte * packet length. * For raw IPv6 datagrams padding is determined from the * lower 2 bits of the GRH:PayLen field.<br> * Note: GRH:PayLen is different from LRH:PktLen. */ public byte[] PayLen(){ byte[] PayLen = { (byte) (content[4] & PayLen_MASK[0]), (byte) (content[5] & PayLen_MASK[1]) }; return PayLen; } /** * <b>Payload length</b> - 16 bits <br> * For an IBA packet this field specifies the number of * bytes starting from the first byte after the GRH, up to and * including the last byte of the ICRC. For a raw IPv6 datagram * this field specifies the number of bytes starting from the * first byte after the GRH, up to but not including either * the VCRC or any padding, to achieve a multiple of 4 byte * packet length. * For raw IPv6 datagrams padding is determined from the * lower 2 bits of the GRH:PayLen field.<br> * Note: GRH:PayLen is different from LRH:PktLen. */ public void setPayLen (byte[] PayLen){ // Throw an error message if the supplied array // is of the wrong length. if (PayLen.length != 2){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ PayLen.length+"!=2"); } content[4] = clear(content[4], PayLen_MASK[0]); content[5] = clear(content[5], PayLen_MASK[1]); content[4] |= (byte) (PayLen[0] & PayLen_MASK[0]); content[5] |= (byte) (PayLen[1] & PayLen_MASK[1]); } /** * * * * <b>Next Header</b> - 8 bits <br> This field identifies the header following the GRH. This field is included for compatibility with IPv6 headers. It should indicate IBA transport. 119 */ public byte NxtHdr(){ return (byte) (content[6] & NxtHdr_MASK); } /** * <b>Next Header</b> - 8 bits <br> * Sets the field that identifies the header following the GRH. * This field is included for conpatibility with IPv6 headers. * It should indicate IBA transport. */ public void setNxtHdr(byte NxtHdr){ content[6] = clear(content[6], NxtHdr_MASK); content[6] |= (byte) (NxtHdr & NxtHdr_MASK); } /** * <b>Hop Limit</b> - 8 bits <br> * This field sets a strict bound on the number of hops between * subnets a packet can make before being discarded. * This is enforced only by routers. */ public byte HopLmt(){ return (byte) (content[7] & HopLmt_MASK); } /** * <b>Hop Limit</b> - 8 bits <br> * Sets the field that sets a strict bound on the number of hops * between subnets a packet can make before being discarded. * This is enforced only by routers. */ public void setHopLmt(byte HopLmt){ content[7] = clear(content[7], HopLmt_MASK); content[7] |= (byte) (HopLmt & HopLmt_MASK); } /** * <b>Source GID</b> - 128 bits <br> * This field identifies the Global Identifier (GID) for the * port which injected the packet into the network. */ public byte[] SGID(){ byte[] SGID = new byte[16]; for (int i=0; i < 16; i++){ SGID[i] |= (byte) (content[i+8] & SGID_MASK[i]); } return SGID; } /** * <b>Source GID</b> - 128 bits <br> * Sets the field that identifies the Global Identifier (GID) * for the port which injected the packet into the network. */ public void setSGID(byte[] SGID){ // Throw an error message if the supplied array // is of the wrong length. if (SGID.length != 16){ throw new IllegalArgumentException("Length of supplied "+ " array is "+ "incorrect! "+ SGID.length+"!=16"); } 120 for (int i=0; i < 16; i++){ content[i+8] = clear(content[i+8], SGID_MASK[i]); content[i+8] |= (byte) (SGID[i] & SGID_MASK[i]); } } /** * <b>Destination GID</b> - 128 bits <br> * This field identifies the GID for the port which will * consume the packet from the network. */ public byte[] DGID(){ byte[] DGID = new byte[16]; for (int i=0; i < 16; i++){ DGID[i] |= (byte) (content[i+24] & DGID_MASK[i]); } return DGID; } /** * <b>Destination GID</b> - 128 bits <br> * This field identifies the GID for the port which * will consume the packet from the network. */ public void setDGID(byte[] DGID){ // Throw an error message if the supplied array // is of the wrong length. if (DGID.length != 16){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ DGID.length+"!=16"); } for (int i=0; i < 16; i++){ content[i+24] = clear(content[i+24], DGID_MASK[i]); content[i+24] |= (byte) (DGID[i] & DGID_MASK[i]); } } /** * Creates an empty GRH (all zeros) */ protected GRH(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } /** * Creates a new GRH from the supplied byte array. * @param content This array must be 40 bytes. */ protected GRH(byte [] content){ // Throw an error message if the supplied array // is of the wrong length. if (content.length != size/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ " array is "+ "incorrect! "+ content.length+"!="+ size/BITS_PER_BYTE); 121 } this.content = content; } /** * Indicates whether this part of the header is * present in this packet. */ protected boolean isPresent(){ return ( lrh.LNH() & lrh.LNH_MASK ) == LNH_IBA_global; } /** Used to mask the IPVer */ protected byte IPVer_MASK = (byte) 0xF0; /** Used to mask the TClass */ protected byte[] TClass_MASK = { (byte) 0x0F, (byte) 0xF0 }; /** Used to mask the FlowLabel */ protected byte[] FlowLabel_MASK = { (byte) 0x0F, (byte) 0xFF, (byte) 0xFF }; /** Used to mask the PayLen */ protected byte[] PayLen_MASK = { (byte) 0xFF, (byte) 0xFF }; /** Used to mask the NxtHdr */ protected byte NxtHdr_MASK = (byte) 0xFF; /** Used to mask the HopLmt */ protected byte HopLmt_MASK = (byte) 0xFF; /** Used to mask the SGID */ protected byte[] SGID_MASK = { (byte) (byte) (byte) (byte) (byte) (byte) (byte) (byte) 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, (byte) (byte) (byte) (byte) (byte) (byte) (byte) (byte) 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, (byte) (byte) (byte) (byte) (byte) (byte) (byte) (byte) 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /** Used to mask the DGID */ protected byte[] DGID_MASK = { (byte) (byte) (byte) (byte) (byte) (byte) (byte) (byte) } /** * <b>The Base Transport Header</b> <br> * *<p> * Base Transport Header (BTH) contains the fields for IBA transports. * The presence of BTH is indicated by the Next Header field of the last * previous header (i.e. either LRH:LNH or GRH:NextHdr depending on * which was the last previous header). *</p> * * Header format: * 0 4 8 12 16 20 24 28 32 * ----------------------------------------------------------------* | OpCode |S|M|Pad| TVer | Partition Key | * | |E| |Cnt| | | 122 * * * * * * * * */ ----------------------------------------------------------------| Reserved | Destination Queue Pair (QP) Number | | (variant) | | ----------------------------------------------------------------|A| Reserved | Packet Sequence Number (PSN) | ----------------------------------------------------------------- public class BTH{ /** Size of the header in bits */ protected int size = BTH_SIZE ; /** The actual bytes that form the header */ protected byte[] content; /** * <b>Opcode</b> - 8 bits <br> * This field indicates the IBA packet type. * The OpCode also specifies which extension headers * follow the Base Transport Header */ public byte OpCode(){ return (byte) (content[0] & OpCode_MASK); } /** * <b>Opcode</b> - 8 bits <br> * Sets the field that indicates the IBA packet type. * The OpCode also specifies which extension headers * follow the Base Transport Header */ public void setOpCode(byte OpCode){ content[0] = clear(content[0], OpCode_MASK); content[0] |= (byte) (OpCode & OpCode_MASK); } /** * <b>Solicited Event</b> - 1 bit <br> * This bit indicates that an event should be generated by * the responder. * The bit values are in the first bit of the byte returned. */ public byte SE(){ return (byte) (content[1] & SE_MASK); } /** * <b>Solicited Event</b> - 1 bit <br> * Sets the bit that indicates that an event should be * generated by the responder. * The bit values should be in the first bit of the byte. */ public void setSE(byte SE){ content[1] = clear(content[1], SE_MASK); content[1] |= (byte) (SE & SE_MASK); } /** * <b>MigReq</b> - 1 bit <br> * This bit is used to communicate migration state. * The bit values are in the second bit of the byte returned. */ public byte M(){ return (byte) (content[1] & M_MASK); } 123 /** * <b>MigReq</b> - 1 bit <br> * This bit is used to communicate migration state. * The bit values should be in the second bit of the byte. */ public void setM(byte M){ content[1] = clear(content[1], M_MASK); content[1] |= (byte) (M & M_MASK); } /** * <b>Pad Count</b> - 2 bits <br> * This field indicates how many extra bytes are added to the * payload to align to a 4 byte boundary * The bit values are in the third and fourth bit of the * byte returned. * @deprecated Use {@link #PadCnt()} instead. */ public byte PadCntInBytes(){ return (byte) (content[1] & PadCnt_MASK); } /** * <b>Pad Count</b> - 2 bits <br> * This field indicates how many extra bytes are added to the * payload to align to a 4 byte boundary * The bit values should be in the third and fourth bit of * the byte. * @deprecated Use {@link #setPadCnt(int PadCnt)} instead. */ public void setPadCntInBytes(byte PadCnt){ content[1] = clear(content[1], PadCnt_MASK); content[1] |= (byte) (PadCnt & PadCnt_MASK); } /** * <b>Pad Count</b> - 2 bits <br> * This field indicates how many extra bytes are added to the * payload to align to a 4 byte boundary * The bit values are in the third and fourth bit of the * byte returned. */ public int PadCnt(){ return (int) ( (content[1] & PadCnt_MASK) >> 4); } /** * <b>Pad Count</b> - 2 bits <br> * This field indicates how many extra bytes are added to the * payload to align to a 4 byte boundary */ public void setPadCnt(int PadCnt){ content[1] = clear(content[1], PadCnt_MASK); content[1] |= (byte) ((PadCnt << 4) & PadCnt_MASK); } /** * <b>Transport Header Version</b> - 4 bits <br> * This field indicates the version of the IBA Transport Headers. * The bit values are in the last four bits of the byte * returned. */ public byte TVer(){ return (byte) (content[1] & TVer_MASK); } 124 /** * <b>Transport Header Version</b> - 4 bits <br> * This field indicates the version of the IBA Transport Headers. * The bit values should be in the last four bits of the byte. */ public void setTVer(byte TVer){ content[1] = clear(content[1], TVer_MASK); content[1] |= (byte) (TVer & TVer_MASK); } /** * <b>Partition Key</b> - 16 bits <br> * This field indicates which logical Partition is associated * with this packet (see <i>10.9 Partitioning</i>, * page 454 in the IBA specification */ public byte[] P_KEY(){ byte[] P_KEY = { (byte) (content[2] & P_KEY_MASK[0]), (byte) (content[3] & P_KEY_MASK[1]) }; return P_KEY; } /** * <b>Partition Key</b> - 16 bits <br> * Sets the field that indicates which logical Partition is * associated with this packet (see <i>10.9 Partitioning</i>, * page 454 in the IBA specification */ public void setP_KEY(byte[] P_KEY){ // Throw an error message if the supplied array // is of the wrong length. if (P_KEY.length != 16/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ P_KEY.length+"!="+ 16/BITS_PER_BYTE); } content[2] = clear(content[2], P_KEY_MASK[0]); content[3] = clear(content[3], P_KEY_MASK[1]); content[2] |= (byte) (P_KEY[0] & P_KEY_MASK[0]); content[3] |= (byte) (P_KEY[1] & P_KEY_MASK[1]); } /** * <b>Destination QP</b> - 24 bits <br> * This field indicates the Work Queue Pair Number (a.k.a QP) * at the destination. */ public byte[] DestQP(){ byte[] DestQP = { (byte) (content[5] & DestQP_MASK[0]), (byte) (content[6] & DestQP_MASK[1]), (byte) (content[7] & DestQP_MASK[2]) }; return DestQP; } /** * <b>Destination QP</b> - 24 bits <br> * Sets the field that indicates the Work Queue Pair Number * (a.k.a QP) at the destination. */ public void setDestQP(byte[] QP){ // Throw an error message if the supplied array // is of the wrong length. 125 if (QP.length != 24/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ " array is "+ "incorrect! "+ QP.length+"!="+ 24/BITS_PER_BYTE); } content[5] = clear(content[5], DestQP_MASK[0]); content[6] = clear(content[6], DestQP_MASK[1]); content[7] = clear(content[7], DestQP_MASK[2]); content[5] |= (byte) (QP[0] & DestQP_MASK[0]); content[6] |= (byte) (QP[1] & DestQP_MASK[1]); content[7] |= (byte) (QP[2] & DestQP_MASK[2]); } /** * <b>Acknowledge request</b> - 1 bit <br> * This bit is used to indicate that an acknowledge (for this * packet) should be scheduled by the responder. * The bit value is in the first bit of the byte returned. */ public byte A(){ return (byte) (content[8] & A_MASK); } /** * <b>Acknowledge request</b> - 1 bit <br> * Sets the bit that is used to indicate that an acknowledge * (for this packet) should be scheduled by the responder. * The bit value should be in the first bit of the byte. */ public void setA(byte A){ content[8] = clear(content[8], A_MASK); content[8] |= (byte) (A & A_MASK); } /** * <b>Packet Sequence Number</b> - 24 bits <br> * This field is used to detect a missing or duplicate Packet. * See <i>9.7.1. Packet Sequence Numbers (PSN)</i> on page 248 * in the IBA specification for a detailed description of PSN. */ public byte[] PSN(){ byte[] PSN = { (byte) (content[9] & PSN_MASK[0]), (byte) (content[10] & PSN_MASK[1]), (byte) (content[11] & PSN_MASK[2]) }; return PSN; } /** * <b>Packet Sequence Number</b> - 24 bits <br> * Sets the field that is used to detect a missing or duplicate * Packet. * See <i>9.7.1. Packet Sequence Numbers (PSN)</i> on page 248 * in the IBA specification for a detailed description of PSN. */ public void setPSN(byte[] PSN){ // Throw an error message if the supplied array // is of the wrong length. if (PSN.length != 24/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ PSN.length+"!="+ 24/BITS_PER_BYTE); 126 } content[9] = clear(content[9], PSN_MASK[0]); content[10] = clear(content[10], PSN_MASK[1]); content[10] = clear(content[10], PSN_MASK[2]); content[9] |= (byte) (PSN[0] & PSN_MASK[0]); content[10] |= (byte) (PSN[1] & PSN_MASK[1]); content[11] |= (byte) (PSN[2] & PSN_MASK[2]); } /** * Creates an empty BTH (all zeros) */ protected BTH(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } /** * Creates a new BTH from the supplied byte array. * @param content This array must be 12 bytes. */ protected BTH(byte [] content){ // Throw an error message if the supplied array // is of the wrong length. if (content.length != size/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ " array is "+ "incorrect! "+ content.length+"!="+ size/BITS_PER_BYTE); } this.content = content; } /** * Indicates whether this part of the header is present * in this packet. */ protected boolean isPresent(){ return ( lrh.LNH() & lrh.LNH_MASK ) == 0x02; } // Masks /** Used to mask the OpCode */ protected final byte OpCode_MASK = (byte) 0xFF; /** Used to mask the SE */ protected final byte SE_MASK = (byte) 0x80; /** Used to mask the M */ protected final byte M_MASK = (byte) 0x40; /** Used to mask the PadCnt */ protected final byte PadCnt_MASK = (byte) 0x30; /** Used to mask the TVer */ protected final byte TVer_MASK = (byte) 0x0F; /** Used to mask the P_KEY */ protected final byte[] P_KEY_MASK = { (byte) 0xFF, (byte) 0xFF }; /** Used to mask the DestQP */ protected final byte[] DestQP_MASK = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }; 127 /** Used to mask the A_MASK */ protected final byte A_MASK = (byte) 0x80; /** Used to mask the PSN */ protected final byte[] PSN_MASK = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }; } /** * <b>The Reliable Datagram Extended Transport Header</b> <br> * *<p> * Reliable Datagram Extended Transport Header (RDETH) contains the * additional transport fields for reliable datagram service. * The RDETH is only in Reliable Datagram packets as indicated by * the Base Transport Header Opcode field. *</p> * * Header format: * *<pre> * 0 4 8 12 16 20 24 28 32 * ----------------------------------------------------------------* | resv | EE-Context | * ----------------------------------------------------------------*</pre> */ public class RDETH{ /** Size of header in bits */ protected int size = RDETH_SIZE; /** The actual bytes that form the header */ protected byte[] content; /** * <b>EE-Context</b> - 24 bits <br> * This field indicates which End-to-End Context should be * used for this Reliable Datagram packet. * <b>NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] EE_Context(){ return null; } /** * Creates an empty RDETH (all zeros) */ protected RDETH(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } /** * Creates a new RDETH from the supplied byte array. * @param content This array must be 4 bytes. */ protected RDETH(byte [] content){ // Throw an error message if the supplied array // is of the wrong length. if (content.length != size/BITS_PER_BYTE){ 128 throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ content.length+"!="+ size/BITS_PER_BYTE); } this.content = content; } /** * Indicates whether this part of the header is present * in this packet. */ protected boolean isPresent(){ return ((bth.OpCode() & 0xE0) == RD_TYPE); } } /** * <b>The Datagram Extended Transport Header (DETH)</b> * *<p> * Datagram Extended Transport Header (DETH) contains the additional * transport fiels for datagram service. The DETH is only in datagram * packets if indicated by the Base Transport Header Opcode field. * * Header format: * *<pre> * 0 4 8 12 16 20 24 28 32 * ----------------------------------------------------------------* | Queue Key | * ----------------------------------------------------------------* | resv 8b | Source QP (SrcQP) 24b | * ----------------------------------------------------------------*</pre> */ public class DETH{ /** Size of header in bits */ protected int size = DETH_SIZE; /** The actual bytes that form the header */ protected byte[] content; /** * <b>Queue Key</b> - 32 bits <br> * This field is requires to authorize access to the * receive queue. * <b>TODO: NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] QueueKey(){ return null; } /** * <b>Reserved</b> - 8 bits <br> * Transmitted as 0, ignored on receive. * <b>TODO: NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] resv(){ return null; 129 } /** * <b>Source QP</b> - 24 bits <br> * This field indicates the Work Queue Pair Number (a.k.a. QP) * at the source. * <b>NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] SrcQP(){ return null; } /** * Creates an empty DETH (all zeros) */ protected DETH(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } /** * Creates a new DETH from the supplied byte array. * @param content This array must be 8 bytes. */ protected DETH(byte [] content){ // Throw an error message if the supplied array // is of the wrong length. if (content.length != size/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ content.length+"!="+ size/BITS_PER_BYTE); } this.content = content; } /** * Indicates whether this part of the header is present * in this packet. */ protected boolean isPresent(){ int lastBits = (bth.OpCode() & 0x1F); return ( (bth.OpCode() & 0xE0) == RD_TYPE && lastBits != RD_RDMA_READ_RESPONSE_FIRST && lastBits != RD_RDMA_READ_RESPONSE_MIDDLE && lastBits != RD_RDMA_READ_RESPONSE_LAST && lastBits != RD_ACKNOWLEDGE && lastBits != RD_ATOMIC_ACKNOWLEDGE ); } } /** * <b>The RDMA Extended Transport Header (RETH)</b> * *<p> * RDMA Extended Transport Header (RETH) contains the additional transport * fields for RDMA operations. The RETH is present in only the first * (or only) packet of an RDMA Request as indicated by the Base Transport 130 * Header Opcode field. *</p> * * Header format: * *<pre> * 0 4 8 12 16 20 24 28 32 * ----------------------------------------------------------------* | Virtual Address 63-32 | * ----------------------------------------------------------------* | Virtual Address 31-0 | * ----------------------------------------------------------------* | Remote Key | * ----------------------------------------------------------------* | DMA Length | * ----------------------------------------------------------------*</pre> */ public class RETH{ /** Size of header in bits */ protected int size = RETH_SIZE; /** The actual bytes that form the header */ protected byte[] content; /** * <b>Virtual Address</b> - 64 bits <br> * This field is the Virtual Address of the RDMA operation. * <b>TODO: NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] VA(){ return null; } /** * <b>Remote Key</b> - 32 bits <br> * This field is the Remote Key that authorizes access * for the RDMA operation. * <b>TODO: NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] R_Key(){ return null; } /** * <b>DMA Length</b> - 32 bits <br> * This field indicates the length (in Bytes) of the * DMA operation. * <b>TODO: NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] DMALen(){ return null; } /** * Creates an empty RETH (all zeros) */ protected RETH(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } 131 /** * Creates a new RETH from the supplied byte array. * @param content This array must be 16 bytes. */ protected RETH(byte [] content){ // Throw an error message if the supplied array // is of the wrong length. if (content.length != size/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ content.length+"!="+ size/BITS_PER_BYTE); } this.content = content; } /** * Indicates whether this part of the header is present * in this packet. * TODO: Not implemented yet, just returns false. */ protected boolean isPresent(){ return false; } } /** *<b>The Atomic Extended Transport Header (AtomicETH)</b> * *<p> * Atomic Extended Transport Header (AtomicETH) contains the * additional transport fields for Atomic packets. The AtomicETH * is only in Atomic packets as indicated by the Base Transport Header * Opcode field. *</p> * * Header format: *<pre> * 0 4 8 12 16 20 24 28 32 * ----------------------------------------------------------------* | Virtual Address 63-32 | * ----------------------------------------------------------------* | Virtual Address 31-0 | * ----------------------------------------------------------------* | Remote Key | * ----------------------------------------------------------------* | Swap (or Add) Data 63-32 | * ----------------------------------------------------------------* | Swap (or Add) Data 31-0 | * ----------------------------------------------------------------* | Compare Data 63-32 | * ----------------------------------------------------------------* | Compare Data 31-0 | * ----------------------------------------------------------------*</pre> */ public class AtomicETH{ 132 /** Size of header in bits */ protected int size = ATOMIC_ETH_SIZE; /** The actual bytes that form the header */ protected byte[] content; /** * <b>Virtual Address</b> - 64 bits <br> * This field is the remote virtual address. * <b>NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] VA(){ return null; } /** * <b>Remote Key</b> - 32 bits <br> * This field is the Remote Key that authorizes access to the * remote virtual address. * <b>NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] R_Key(){ return null; } /** * <b>Swap (or Add) Data</b> - 64 bits <br> * This field is an operand in atomic operations. * <b>NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] SwapDt(){ return null; } /** * <b>Compare Data</b> - 64 bits <br> * This field is an operand in CmpSwap atomic operation. * <b>NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] CmpDt(){ return null; } /** * Creates an empty AtomicETH (all zeros) */ protected AtomicETH(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } /** * Creates a new AtomicETH from the supplied byte array. * @param content This array must be 28 bytes. */ protected AtomicETH(byte [] content){ // Throw an error message if the supplied array // is of the wrong length. if (content.length != size/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ content.length+"!="+ size/BITS_PER_BYTE); } 133 this.content = content; } /** * Indicates whether this part of the header is present * in this packet. * TODO: Not implemented yet, just returns false. */ protected boolean isPresent(){ return false; } } /** *<b>The ACK Extended Transport Header (AETH)</b> * *<p> * ACK Extended Transport Header (AETH) contains the additional * transport fields for ACK packets. The AETH is only in Acknowledge, * RDMA READ Response First, RDMA Response Response Last, and RDMA * READ Response Only packets as indicated by the Base Transport * Header Opcode field. *</p> * * Header format: * *<pre> * 0 4 8 12 16 20 24 28 32 * ----------------------------------------------------------------* | Syndrome | Message Sequence Number | * ----------------------------------------------------------------*</pre> * */ public class AETH{ /** Size of header in bits */ protected int size = AETH_SIZE; /** The actual bytes that form the header */ protected byte[] content; /** * <b>Syndrome</b> - 8 bits <br> * This field indicates if this is an ACK or NAK packet plus * additional information about the ACK or NAK. * <b>NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] Syndrome(){ return null; } /** * <b>Message Sequence Number</b> - 24 bits <br> * This field indicates the sequence number of the last * message completed at the responder. * <b>NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] MSN(){ return null; } 134 /** * Creates an empty AETH (all zeros) */ protected AETH(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } /** * Creates a new AETH from the supplied byte array. * @param content This array must be 4 bytes. */ protected AETH(byte [] content){ // Throw an error message if the supplied array // is of the wrong length. if (content.length != size/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ content.length+"!="+ size/BITS_PER_BYTE); } this.content = content; } /** * Indicates whether this part of the header is present in * this packet. * TODO: Not implemented yet, just returns false. */ protected boolean isPresent(){ return false; } } /** *<b>The Atomic ACK Extended Transport Header (AtomicAckETH)</b> * *<p> * Atomic ACK Extended Transport Header (AtomicAckETH) contains the * additional transport fields for AtomicACK packets. The AtomicAckETH * is only in Atomic Acknowledge packets as indicated by the Base Transport * Header Opcode field. *</p> * * Header format: * *<pre> * 0 4 8 12 16 20 24 28 32 * ----------------------------------------------------------------* | Original Remote Data 63-32 | * ----------------------------------------------------------------* | Original Remote data 31-0 | * ----------------------------------------------------------------*</pre> * */ public class AtomicAckETH{ 135 /** Size of header in bits */ protected int size = ATOMIC_ACK_ETH_SIZE; /** The actual bytes that form the header */ protected byte[] content; /** * <b>Original Remote Data</b> - 64 bits <br> * This field is the return operand in atomic operations and * contains the data in the remote memory location before * atomic operation. * <b>TODO: NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] OrigRemDt(){ return null; } /** * Creates an empty AtomicAckETH (all zeros) */ protected AtomicAckETH(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } /** * Creates a new AtomicAckETH from the supplied byte array. * @param content This array must be 8 bytes. */ protected AtomicAckETH(byte [] content){ // Throw an error message if the supplied array // is of the wrong length. if (content.length != size/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ content.length+"!="+ size/BITS_PER_BYTE); } this.content = content; } /** * Indicates whether this part of the header is present in * this packet. * TODO: Not implemented yet, just returns false. */ protected boolean isPresent(){ return false; } } /** * <b>The Immediate Data Extended Transport Header (ImmDt)</b> * *<p> * Immediate Data Extended Transport Header (ImmDt) contains the * additional data that is placed in the receive Completion Queue 136 * Element (CQE). The ImmDt is only in Send or RDMA-Write packets * with Immediate Data if indicated by the Base Transport Header Opcode. *</p> * * Header format: * *<pre> * 0 4 8 12 16 20 24 28 32 * ----------------------------------------------------------------* | Immediate Data | * ----------------------------------------------------------------*</pre> * */ public class ImmDt{ /** Size of header in bits */ protected int size = IMM_DT_SIZE; /** The actual bytes that form the header */ protected byte[] content; /** *The immediate data * <b>TODO: NOT IMPLEMENTED YET. RETURNS NULL!</b> */ protected byte[] immDt(){ return null; } /** * Creates an empty ImmDt (all zeros) */ protected ImmDt(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } /** * Creates a new ImmDt from the supplied byte array. * @param content This array must be 4 bytes. */ protected ImmDt(byte [] content){ // Throw an error message if the supplied array // is of the wrong length. if (content.length != size/BITS_PER_BYTE){ throw new IllegalArgumentException("Length of supplied "+ "array is incorrect! "+ content.length+"!="+ size/BITS_PER_BYTE); } this.content = content; } /** * Indicates whether this part of the header is present * in this packet. * TODO: Not implemented yet, just returns false. */ 137 protected boolean isPresent(){ return false; } } /** * The payload of the packet */ public class PYLD{ /** Size of payload in bits */ protected int size; /** The actual bytes that form the header */ protected byte[] content; /** * Creates an empty Payload */ protected PYLD(){ // Create an empty content array this.content = new byte[size / BITS_PER_BYTE]; } /** * Creates a new Payload from the supplied byte array. * @param content This array must be 4 bytes. */ protected PYLD(byte [] content){ setContent(content); } /* * Sets the content. Adds padding if necessary. * (see 5.2.11, <i>Payload</i> ( page 134) in the * IBA specification. */ public void setContent(byte[] content){ int i=0; int sizeInBytes = content.length; // Add padding if necessary int padding = (4 - (content.length % 4)); if ( padding > 0 && padding < 4){ sizeInBytes += padding; bth.setPadCnt(padding); } // Fill in contents this.content = new byte[sizeInBytes]; for (i=0; i < content.length; i++){ this.content[i] = content[i]; } // Padding should be zeros for (; i < sizeInBytes; i++){ this.content[i] = 0; } this.size = sizeInBytes * BITS_PER_BYTE; } /* * Returns the content (without the padding) */ public byte[] getContentWithoutPadding(){ 138 int sizeInBytes; byte[] retVal; sizeInBytes = this.content.length; sizeInBytes -= bth.PadCnt(); retVal = new byte[sizeInBytes]; // Save a for loop if there’s no padding if (sizeInBytes == this.content.length){ return this.content; } // Fill up with the data (without the padding) for (int i=0; i < sizeInBytes; i++){ retVal[i] = this.content[i]; } return retVal; } /** * Indicates whether this part of the header is present * in this packet. * Always returns true. */ protected boolean isPresent(){ return true; } } /** * Dummy class representing the ICRC bits of the packet. * Contains only zeros. */ public class ICRC{ /** Size of header in bits */ protected int size = ICRC_SIZE; protected byte[] content; protected ICRC(){ this.content = new byte[size / BITS_PER_BYTE]; } protected ICRC(byte[] content){ this.content = content; } /** * Indicates whether this part of the header is present * in this packet. */ protected boolean isPresent(){ return ( lrh.LNH() != LNH_raw ); } } /** * Dummy class representing the VCRC bits of the packet. * Contains only zeros. */ public class VCRC{ /** Size of header in bits */ protected int size = VCRC_SIZE; 139 protected byte[] content; protected VCRC(){ this.content = new byte[size / BITS_PER_BYTE]; } protected VCRC(byte[] content){ this.content = content; } /** * Indicates whether this part of the header is present * in this packet. * Always returns true. */ protected boolean isPresent(){ return true; } } public IBAPacket(){ lrh = new LRH(); grh = new GRH(); bth = new BTH(); rdeth = new RDETH(); deth = new DETH(); reth = new RETH(); atomicETH = new AtomicETH(); atomicAckETH = new AtomicAckETH(); aeth = new AETH(); immDt = new ImmDt(); pyld = new PYLD(); icrc = new ICRC(); vcrc = new VCRC(); } public IBAPacket(byte[] content){ this(); byte[] tmpByte; // The current index in the content array int currentIndex = 0; // Fetch the various fields from the bytes supplied // LRH tmpByte = new byte[LRH_SIZE/BITS_PER_BYTE]; for (int i=0; i < LRH_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } lrh = new LRH(tmpByte); // if GRH (lrh.LNH() == LNH_IBA_global){ tmpByte = new byte[GRH_SIZE/BITS_PER_BYTE]; for (int i=0; i < GRH_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } grh = new GRH(tmpByte); } // BTH if (lrh.LNH() != LNH_raw){ tmpByte = new byte[BTH_SIZE/BITS_PER_BYTE]; for (int i=0; i < BTH_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } bth = new BTH(tmpByte); 140 } // Just to make indexing of ’incFields’ more intuitive final final final final final final final final final final int int int int int int int int int int nRDETH = 0; nDETH = 1; nRETH = 2; nAtomicETH = 3; nAETH = 4; nAtomicAckETH = 5; nImmDt = 6; nPYLD = 7; nICRC = 8; nVCRC = 9; // different types of fields boolean[] incFields = new boolean[10]; for (int i=0; i < incFields.length; i++){ incFields[i] = false; } // RDETH and onwards. See pp. 208 and onward in the IBA spec // for information on how the packet is to be interpreted. // All that have OpCode[7-5] (first 3 bits) other than 010 // are Connection Oriented. This is not implemented. int firstBits = (bth.OpCode() & 0xE0) >> 5; int lastBits = (bth.OpCode() & 0x1F); if ( firstBits != 2){ Reporter.error("IBAPacket.IBAPacket(byte[]) : "+ "Undefined or unimplemented "+ "packet contents defined in OpCode "+ "field of BTH: "+ Utils.asBitString(bth.OpCode())); } else if if ( ( firstBits == 2 ){ lastBits >= 0x16 && // Impossible, but anyhow... : "+ "Undefined packet contents defined " +"in OpCode field of BTH."+ lastBits <= 0x1F ){ Reporter.error("IBAPacket.IBAPacket(byte[]) Utils.asBitString(bth.OpCode())); } // TODO: Do the right thing here depending on the contents // of the packet. Check that every case is covered. // All have ICRC and VCRC attached incFields[nICRC] = true; incFields[nVCRC] = true; switch(lastBits){ case 0: // SEND case 1: // SEND case 2: // SEND case 4: // SEND case 7: // RDMA case 8: // RDMA First Middle Last Only WRITE Middle WRITE Last incFields[nRDETH] = true; incFields[nDETH] = true; incFields[nPYLD] = true; break; case 3: // SEND Last with Immediate case 5: // SEND Only with Immediate case 9: // RDMA WRITE Last with Immediate incFields[nRDETH] = true; incFields[nDETH] = true; incFields[nImmDt] = true; 141 incFields[nPYLD] = true; break; case 6: // RDMA WRITE First case 10: // RDMA WRITE Only incFields[nRDETH] = true; incFields[nDETH] = true; incFields[nRETH] = true; incFields[nPYLD] = true; break; case 11: // RDMA WRITE Only with Immediate incFields[nRDETH] = true; incFields[nDETH] = true; incFields[nRETH] = true; incFields[nImmDt] = true; incFields[nPYLD] = true; break; case 12: // RDMA READ Request incFields[nRDETH] = true; incFields[nDETH] = true; incFields[nRETH] = true; break; case 13: // RDMA READ response First case 15: // RDMA READ response Last case 16: // RDMA READ responsy Only incFields[nRDETH] = true; incFields[nAETH] = true; incFields[nPYLD] = true; break; case 14: // RDMA READ response Middle incFields[nRDETH] = true; incFields[nPYLD] = true; break; case 17: // Acknowledge incFields[nRDETH] = true; incFields[nAETH] = true; break; case 18: // ATOMIC Acknowledge incFields[nRDETH] = true; incFields[nAETH] = true; incFields[nAtomicAckETH] = true; break; case 19: // CmpSwap case 20: // FetchAdd incFields[nRDETH] = true; incFields[nDETH] = true; incFields[nAtomicETH] = true; break; case 21: // RESYNC incFields[nRDETH] = true; incFields[nDETH] = true; break; } } // Loop through all the headers and add them is they are present. // RDETH if (incFields[nRDETH]){ tmpByte = new byte[RDETH_SIZE/BITS_PER_BYTE]; for (int i=0; i < RDETH_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } rdeth = new RDETH(tmpByte); } // DETH if (incFields[nDETH]){ tmpByte = new byte[DETH_SIZE/BITS_PER_BYTE]; 142 for (int i=0; i < DETH_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } deth = new DETH(tmpByte); } // RETH if (incFields[nRETH]){ tmpByte = new byte[RETH_SIZE/BITS_PER_BYTE]; for (int i=0; i < RETH_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } reth = new RETH(tmpByte); } // AtomicETH if (incFields[nAtomicETH]){ tmpByte = new byte[ATOMIC_ETH_SIZE/BITS_PER_BYTE]; for (int i=0; i < ATOMIC_ETH_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } atomicETH = new AtomicETH(tmpByte); } // AETH if (incFields[nAETH]){ tmpByte = new byte[AETH_SIZE/BITS_PER_BYTE]; for (int i=0; i < AETH_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } aeth = new AETH(tmpByte); } // AtomicAckETH if (incFields[nAtomicAckETH]){ tmpByte = new byte[ATOMIC_ACK_ETH_SIZE/BITS_PER_BYTE]; for (int i=0; i < ATOMIC_ACK_ETH_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } atomicAckETH = new AtomicAckETH(tmpByte); } // ImmDt if (incFields[nImmDt]){ tmpByte = new byte[IMM_DT_SIZE/BITS_PER_BYTE]; for (int i=0; i < IMM_DT_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } immDt = new ImmDt(tmpByte); } // PYLD if (incFields[nPYLD]){ int payloadSize = content.length - (currentIndex + ((ICRC_SIZE + VCRC_SIZE) / BITS_PER_BYTE)); tmpByte = new byte[payloadSize]; for (int i=0; i < payloadSize; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } pyld = new PYLD(tmpByte); } // ICRC if (incFields[nICRC]){ 143 tmpByte = new byte[ICRC_SIZE/BITS_PER_BYTE]; for (int i=0; i < ICRC_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } icrc = new ICRC(tmpByte); } // VCRC if (incFields[nVCRC]){ tmpByte = new byte[VCRC_SIZE/BITS_PER_BYTE]; for (int i=0; i < VCRC_SIZE/BITS_PER_BYTE; i++, currentIndex++){ tmpByte[i] = content[currentIndex]; } vcrc = new VCRC(tmpByte); } } /** * Clears a byte, but just some of the bits. * Used to clear a value in a byte that’s shared between * two fields without wiping out the other field. * @param content The byte to celar a part of * @param mask The mask use to mask out the bits in question. * E.g. clear(0xF1, 0x0F) returns 0xF0). */ private byte clear (byte content, byte mask){ return (byte) (content & (byte) (0xFF ^ mask) & 0xFF); } /** * Updates the packet length field in the lrh * based on the contents of the rest of the packet. */ public void updatePacketLength(){ int packetLength = 0; // Could be a totally "empty" packet if ((lrh.LNH() & lrh.LNH_MASK) == 0x00 && (lrh.DLID()[0] & lrh.DLID_MASK[0]) (lrh.DLID()[1] & lrh.DLID_MASK[1]) (lrh.SLID()[0] & lrh.SLID_MASK[0]) (lrh.SLID()[1] & lrh.SLID_MASK[1]) lrh.setPktLen(0); == == == == 0x00 && 0x00 && 0x00 && 0x00){ return; } // Add the LRH packetLength += lrh.size; // Add the GRH if present if (grh.isPresent()){ packetLength += grh.size; } // Add the BTH if present if (bth.isPresent()){ packetLength += bth.size; } // Add the RDETH if present if (rdeth.isPresent()){ packetLength += rdeth.size; 144 } // Add the DETH if present if (deth.isPresent()){ packetLength += deth.size; } // Add the RETH if present if (reth.isPresent()){ packetLength += reth.size; } // Add the AtomicETH if present if (atomicETH.isPresent()){ packetLength += atomicETH.size; } // Add the AETH if present if (aeth.isPresent()){ packetLength += aeth.size; } // Add the AtomicAckETH if present if (atomicAckETH.isPresent()){ packetLength += atomicAckETH.size; } // Add the ImmDt if present if (immDt.isPresent()){ packetLength += immDt.size; } // Add the PYLD if present if (pyld.isPresent()){ packetLength += pyld.size; } // Add the ICRC if present if (icrc.isPresent()){ packetLength += icrc.size; } // Add the VCRC if present if (vcrc.isPresent()){ packetLength += vcrc.size; } // Divide by BITS_PER_BYTE and 4 to get number of words packetLength /= (BITS_PER_BYTE * 4); // Update the header field lrh.setPktLen(packetLength); } /** * Creates a new IBAPacket with the supplied destination * and source address. * @param destination - the DLID in the LRH * @param source - the SLID in the LRH */ public IBAPacket(Address destination, Address source){ this(); // Fill inn the source and destination addresses in the LRH lrh.setDLID(destination.getAddress()); lrh.setSLID(source.getAddress()); } 145 /** * Returns the content of this IBA packet. */ public byte[] getContent(){ int pktLen = lrh.PktLen(); // +2 for VCRC, which is not included in the // packet length field. byte[] content = new byte[pktLen * 4 + 2 ]; int currentIndex = 0; int i; // Add the LRH for (i=0; i < lrh.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = lrh.content[i]; } // Add the GRH if present if (grh.isPresent()){ for (i=0; i < grh.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = grh.content[i]; } } // Add the BTH if present if (bth.isPresent()){ for (i=0; i < bth.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = bth.content[i]; } } // Add the RDETH if present if (rdeth.isPresent()){ for (i=0; i < rdeth.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = rdeth.content[i]; } } // Add the DETH if present if (deth.isPresent()){ for (i=0; i < deth.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = deth.content[i]; } } // Add the RETH if present if (reth.isPresent()){ for (i=0; i < reth.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = reth.content[i]; } } // Add the AtomicETH if present if (atomicETH.isPresent()){ for (i=0; i < atomicETH.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = atomicETH.content[i]; } } // Add the AETH if present if (aeth.isPresent()){ for (i=0; i < aeth.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = aeth.content[i]; } } // Add the AtomicAckETH if present 146 if (atomicAckETH.isPresent()){ for (i=0; i < atomicAckETH.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = atomicAckETH.content[i]; } } // Add the ImmDt if present if (immDt.isPresent()){ for (i=0; i < immDt.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = immDt.content[i]; } } // Add the PYLD if present if (pyld.isPresent()){ for (i=0; i < pyld.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = pyld.content[i]; } } // Add the ICRC if present if (icrc.isPresent()){ for (i=0; i < icrc.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = icrc.content[i]; } } // Add the VCRC if present if (vcrc.isPresent()){ for (i=0; i < vcrc.size / BITS_PER_BYTE; i++, currentIndex++){ content[currentIndex] = vcrc.content[i]; } } return content; } /** * Returns the full size of the packet in bytes, including all headers * and CRC fields and padding. */ public int getSize(){ int pktLen; updatePacketLength(); // Empty packet, return 0 size if (this.lrh.PktLen() == 0 ) return 0; // +2 for the VCRC, not included in PktLen. pktLen = (this.lrh.PktLen() * 4) + 2; return pktLen; } /** The Local Route Header (LRH) of this Packet */ public LRH lrh; /** The Global Route Header (GRH) of this Packet */ public GRH grh; /** The Base Transport Header (BTH) of this Packet */ public BTH bth; /** * The Reliable Datagram Extended Transport Header 147 * (RDETH) of this Packet */ public RDETH rdeth; /** The Datagram Extended Transport Header (DETH) of this Packet */ public DETH deth; /** The RDMA Extended Transport Header (RETH) of this Packet */ public RETH reth; /** The Atomic Extended Transport Header (AtomicETH) of this Packet */ public AtomicETH atomicETH; /** * The Atomic ACK Extended Transport Header * (AtomicAckETH) of this Packet */ public AtomicAckETH atomicAckETH; /** The ACK Extended Transport Header (AETH of this Packet */ public AETH aeth; /** * The Immediate Data Extended Transport Header * (ImmDt) of this Packet */ public ImmDt immDt; /** The Payload of this Packet */ public PYLD pyld; /** The ICRC of this Packet */ public ICRC icrc; /** The VCRC of this Packet */ public VCRC vcrc; } 148 no.uio.ifi.gruns.infiniband.IBAInputPort package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; /** * The input ports on IBA units */ public class IBAInputPort extends InputPort { /** * Creates a new IBA input port */ public IBAInputPort(Kernel kernel){ super(kernel); init(); } /** * Creates a new IBA input port with the supplied number of VLs * @param numberOfVirtualLanes The number of VLs supported * @param bufferSize The size of the buffers on each VL * @param kernel The kernel to use for simulation */ public IBAInputPort(int numberOfVirtualLanes, int bufferSize, Kernel kernel){ super(numberOfVirtualLanes, kernel); this.bufferSize = bufferSize; init(); ABR = new int[numberOfVirtualLanes]; numberOfBytesOnTheirWay = new int[numberOfVirtualLanes]; } /** * Creates a new IBA input port with the supplied number of VLs */ public IBAInputPort(int numberOfVirtualLanes, Kernel kernel){ super(numberOfVirtualLanes, kernel); bufferSize = 4 * Integer.parseInt(System.getProperty("infiniband.REAL_PACKET_SIZE")); // Just to keep track of things System.setProperty("infiniband.INPUT_PORT_BUFSIZE", ""+bufferSize); init(); ABR = new int[numberOfVirtualLanes]; numberOfBytesOnTheirWay = new int[numberOfVirtualLanes]; } /** Common initialization */ protected void init(){ currentLRH = dummyPacket.new LRH(); // Need to reallocate buffers to get the right size for (int i=0; i < numberOfVLs; i++){ buffer[i] = new ByteBuffer(bufferSize); } } /** * Receives a packet from the supplied virtual lane number. * TODO: Not implemented. Check if we actually need this or not. 149 */ public Packet receivePacket(int VLNumber){ return null; } /** * Receives a packet from the current virtual lane. Not implemented. */ public Packet receivePacket(){ return null; } /** * Indicate whether we have data on the VL supplied */ public boolean hasDataOnVL(int VLNumber){ return !buffer[VLNumber].isEmpty(); } /** * Receives a single byte from the supplied virtual channel number */ public byte receiveByte(int VLNumber){ return buffer[VLNumber].get(); } /** * Returns the available credits on this port to simulate * the Flow Control Packet as described in section 7.9.4 * on pp. 183-> in the IBA specification. * FCCL is the Flow Control Credit Limit. * * TODO: Be aware that there could be some bytes on * the link which have not * yet arrived. This shouldn’t be possible, because the FCCL is really * sent over the link, and, hence can’t overtake the bytes * already on the link. * * OK. This should be solved with the "bytesOnTheirWay" variable. * * @param FCTBS - the Flow Control Total Blocks Sent * @param VLNumber - the number of the VL to get the FCCL * for. */ public int getFCCL(int FCTBS, int VLNumber){ // ABR Should be set to the FCTBS when a Flow // control packet is received. // These numbers should not differ when we have // no link loss, as is the case here. ABR[VLNumber] = FCTBS; int FCCL = 0; // // // // // // TODO: Maybe this must be updated to reflect that the blocks do not contain the LRH or the VCRC. How do we know the largest overhead of packets received? This decides how many blocks we can receive. Currently assumes IBA packets only, and that the packets have all headers present. // Subtract the bytes in transfer, so that we don’t 150 // say we have more buffer space that we actually have. int capacity = buffer[VLNumber].getRemainingCapacity() - numberOfBytesOnTheirWay[VLNumber]; // If we have room for 3.5 blocks, we only have room for 3, // hence the floor. int bufferBlockCapacity = (int) Math.floor(capacity / Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE"))); if (bufferBlockCapacity >= 2048){ FCCL = (ABR[VLNumber] + 2048) % 4096; }else{ FCCL = (ABR[VLNumber] + bufferBlockCapacity) % 4096; } return FCCL; } /** * Used to tell this unit how many bytes are in transit to * this unit, so that the getFCCL method can subtract the bytes * in transit from the buffer capacity when replying to a * @link{#getFCCL} message. * * This is necessary because the FCCL is not sent as a real message. * If it were, it would be guaranteed to reach us after * we had received all the bytes we had promised that we have * the buffer space to receive. */ public void increaseBytesSent(int numberOfBytes, int VLNumber){ this.numberOfBytesOnTheirWay[VLNumber] += numberOfBytes; } /** * Receives a number of bytes from the supplied buffer (internally in the * Input port) */ private void readBytes(int VLNumber, byte[] fromBuffer, int numBytes){ int i; for (i=0; i < numBytes; i++){ buffer[VLNumber].put(fromBuffer[i]); } // update the number of bytes on their way this.numberOfBytesOnTheirWay[VLNumber]-=i; // update the number of bytes received from current packet this.numberOfBytesReceived+=i; // update the number of bytes received this turn this.bytesReceivedThisTurn+=i; } /** * Receives a number of bytes from the supplied link (internally in the * Input port) */ private void readBytes(int VLNumber, Link link, int numBytes){ int i; for (i=0; i < numBytes; i++){ if (link.isEmpty()){ Reporter.error("IBAInputPort.readBytes(int, link, int): "Link is empty, "+i+" bytes sent, trying } buffer[VLNumber].put(link.receiveByte()); } // update the number of bytes on their way 151 "+ to send "+numBytes); this.numberOfBytesOnTheirWay[VLNumber]-=i; // update the number of bytes received from current packet this.numberOfBytesReceived+=i; // update the number of bytes received this turn this.bytesReceivedThisTurn+=i; } public void act(){ int numBytes=0, pktLen=0; byte [] tmpByte = null; // reset number of bytes received this turn this.bytesReceivedThisTurn = 0; switch(state){ case READY: this.numberOfBytesReceived = 0; this.currentLRH = null; if (link.isEmpty()){ // Halt with an error instead of rescheduling Reporter.error("IBAInputPort.act(): "+ "Trying to receive from empty link!"); // Not enough // kernel.schedule(this, kernel.currentTime() + 1); // break; } else{ state = RECEIVING_HEAD; } // fallthrough case RECEIVING_HEAD: // Check if we have already read the LRH, // if not, read it. if (currentLRH == null){ tmpByte = new byte[IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE]; // Receive LRH for (int i=0; i < tmpByte.length; i++){ tmpByte[i] = link.receiveByte(); } currentLRH = dummyPacket.new LRH(tmpByte); } /* // DEBUG if (ID.startsWith("Switch 0, input port")){ Reporter.println("Time: "+kernel.currentTime()+", "+ID+": "+ "Receiving header"+ " on VL "+currentLRH.VL()); } // */ // put the LRH in the right buffer if there is space for it. if (buffer[currentLRH.VL()].getRemainingCapacity() >= IBAPacket.LRH_SIZE){ if (tmpByte == null){ tmpByte = currentLRH.content; } readBytes(currentLRH.VL(), tmpByte, tmpByte.length); 152 } // Reschedule if we didn’t have enough space for the LRH. else{ // Halt Reporter.error("IBAInputPort.act: Not enough buffer space!"); // Not good enough to reschedule // kernel.schedule(this, kernel.currentTime() + // Integer.parseInt(System.getProperty("infiniband.SLEEP_TIME"))); // break; } // Read more data from the link // (up to Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE")) bytes) // if there’s space. numBytes = Math.min(Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE")) - tmpByte.length, IBAPacketParser.packetLength(currentLRH)); if (buffer[currentLRH.VL()].getRemainingCapacity() >= numBytes){ readBytes(currentLRH.VL(), link, numBytes); } else{ Reporter.error("IBAInputPort.act: Not enough buffer space!"); } // Update the state. We have read the head if we get here. state = RECEIVING_CONTENTS; pktLen = IBAPacketParser.packetLength(currentLRH); // Check if we are finished reading the packet // (shouldn’t really be possible) if (numberOfBytesReceived == pktLen){ state = READY; } break; case RECEIVING_CONTENTS: pktLen = IBAPacketParser.packetLength(currentLRH); // Maybe there’s less than IBA_BLOCK_SIZE to receive numBytes = Math.min(Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE")), pktLen-numberOfBytesReceived); // Reschedule on the next step if there’s not enough // space to read. if (buffer[currentLRH.VL()].getRemainingCapacity() < numBytes){ // Halt Reporter.error("IBAInputPort.act: Not enough buffer space!"); // Not good enough to reschedule // kernel.schedule(this, kernel.currentTime() + // Integer.parseInt(System.getProperty("infiniband.SLEEP_TIME"))); // break; } // Read data from the link readBytes(currentLRH.VL(), link, numBytes); /* // DEBUG if (ID.startsWith("Switch 0, input port")){ Reporter.println("Time: "+kernel.currentTime()+", "+ID+": "+ "Receiving block "+(int)(Math.ceil( ((double)numberOfBytesReceived)/64))+ " on VL "+currentLRH.VL()); } 153 // */ // Check if we are finished reading the packet if (numberOfBytesReceived == pktLen){ state = READY; } break; default: Reporter.error("IBAInputPort.act: Illegal state: "+state); } // // // // // if Notify the unit we are attached to by scheduling it, so that it can read the data that has come in. Maybe the unit wants to start reading before a whole packet has arrived, i.e. if it’s a switch, and wants to start looking up the address. (bytesReceivedThisTurn != 0){ /* // DEBUG if (ID.equals("Drain 0, port 0")){ Reporter.debugln(ID+", VL "+currentLRH.VL()+ ": bytesReceivedThisTurn = "+bytesReceivedThisTurn+ ", Bytes of current packet: "+ numberOfBytesReceived); } // */ if (sink instanceof IBADrainNode){ ((IBADrainNode)sink).increaseBytesReadyToBeReceived(this, currentLRH.VL(), bytesReceivedThisTurn); } kernel.schedule(sink, kernel.currentTime() + 1); } /* // DEBUG if (this.ID.startsWith("Switch 4")){ Reporter.debugln(ID+", received "+numberOfBytesReceived+" from packet, "+ bytesReceivedThisTurn+" this turn."); } // */ /* // DEBUG Reporter.debugln("--------------------------------------------------"); Reporter.debugln("Time: "+kernel.currentTime()+", ID: "+ID); Reporter.debugln("numBytes that should have been read = "+numBytes); Reporter.debugln("bytesReceivedThisTurn = "+bytesReceivedThisTurn); Reporter.debugln("bytes received from current = "+numberOfBytesReceived); // */ } /** * Returns buffer number VLNumber. */ public ByteBuffer getByteBuffer(int VLNumber){ 154 return buffer[VLNumber]; } public String report(){ return "IBAInputPort \""+ID+"\": bytes in buffer : "+ numberOfBytesReceived+ ", VL: "+(currentLRH==null?"null":""+currentLRH.VL())+ ",bytes on their way[0]: "+ this.numberOfBytesOnTheirWay[0]; } /** The number of bytes received of the current packet */ protected int numberOfBytesReceived; /** The number of bytes received this turn. */ protected int bytesReceivedThisTurn; /** The head of the packet we are currently receiving */ protected IBAPacket.LRH currentLRH; /** * The state of the input port. * Either READY, RECEIVING_HEAD or RECEIVING_CONTENTS */ protected int state; /** * The Adjusted Blocks Received, number of blocks received * represented by a 12-byte number (i.e. <4096), for each * VL in the input port. */ protected int[] ABR; /** * The unit that this input port is receiving for */ protected Scheduleable sink; /** * Sets the unit that this input port is receiving for */ protected void setSink(Scheduleable sink){ this.sink = sink; } /** * Gets the unit that this input port is receiving for */ protected Scheduleable getSink(){ return sink; } /** * Gets the unit that this input port is receiving from */ protected Scheduleable getSource(){ return link.getSource(); } /** * * * * * * Used to tell this unit how many bytes are in transit to this unit, so that the getFCCL method can subtract the bytes in transit from the buffer capacity when replying to a @link{#getFCCL} message. This is necessary because the FCCL is not sent as a real message. 155 * If it were, it would be guaranteed to reach us after we * had received all the bytes we had promised that we have the * buffer space to receive. */ protected int[] numberOfBytesOnTheirWay; protected static final int READY = 0; protected static final int RECEIVING_HEAD = 1; protected static final int RECEIVING_CONTENTS = 2; protected static final IBAPacket dummyPacket = new IBAPacket(); } 156 no.uio.ifi.gruns.infiniband.IBAPacketSender package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; /** * All classes that are able to send IBA packets * should implement this interface. */ public interface IBAPacketSender extends PacketSender { /** * Adds an entry of high priority on this output port. * @param portNumber The output port to add an entry to * @param VLNumber The Virtual lane to add an entry for * @param weight The number of * Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE"))-byte units this VL should * be allowed to transfer on each turn * */ public void addHighPriority(int portNumber, int VLNumber, int weight); /** * Adds an entry of low priority on this output port. * @param portNumber The output port to add an entry to * @param VLNumber The Virtual lane to add an entry for * @param weight The number of * Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE"))-byte units this VL should * be allowed to transfer on each turn * */ public void addLowPriority(int portNumber, int VLNumber, int weight); } 157 no.uio.ifi.gruns.infiniband.IBASwitch package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; import no.uio.ifi.tools.*; import java.util.Random; import java.util.ArrayList; public class IBASwitch extends Switch implements Reporting{ public IBASwitch(Kernel kernel){ super(kernel); routingTable = new IBARoutingTable(); } public IBASwitch(int numberOfInputPorts, int numberOfOutputPorts, int numberOfVirtualLanes, Kernel kernel){ this(numberOfInputPorts, numberOfOutputPorts, numberOfVirtualLanes, "", kernel); } /** * @param numberOfInputPorts The number of input ports * @param numberOfOutputPorts The number of output ports * @param inputBufferSize The size of the buffers in the input * ports. Affects how the switch acts, * because when the output buffers go full, the low * priority queues get more priority than when * there’s still plenty of space. * @param outputBufferSize The size of the buffers in the output * ports. * @param numberOfVirtualLanes The number of virtual lanes on * input and output ports. * @param ID A string identifying the switch * @param kernel The kernel to use for simulation */ public IBASwitch(int numberOfInputPorts, int numberOfOutputPorts, int numberOfVirtualLanes, int inputBufferSize, int outputBufferSize, String ID, Kernel kernel){ this(kernel); int i; this.inputBufferSize = inputBufferSize; this.outputBufferSize = outputBufferSize; // Create input and output ports inputPorts = new IBAInputPort[numberOfInputPorts]; for (i=0; i < numberOfInputPorts; i++){ inputPorts[i] = new IBAInputPort(numberOfVirtualLanes, inputBufferSize, kernel); inputPorts[i].setSink(this); 158 inputPorts[i].setID(ID+", input port "+i); } outputPorts = new IBAOutputPort[numberOfOutputPorts]; for (i=0; i < numberOfOutputPorts; i++){ outputPorts[i] = new IBAOutputPort(numberOfVirtualLanes, outputBufferSize, kernel); outputPorts[i].setID(ID+", output port "+i); } // Assign ID this.ID = ID; // Other initialization VLstates = new VLstate[inputPorts.length][numberOfVirtualLanes]; for (i=0; i < VLstates.length; i++){ for (int j=0; j < VLstates[i].length; j++){ VLstates[i][j] = new VLstate(); } } random = new Random(1L); drawHistory = new int[inputPorts.length]; // prefs this.procTime = Integer.getInteger("infiniband.PACKET_PROCESSING_TIME").intValue(); this.sleepTime = Integer.getInteger("infiniband.SLEEP_TIME").intValue(); this.blockSize = Integer.getInteger("infiniband.BLOCK_SIZE").intValue(); } /** * Uses the default bufferSize of the input and output ports. */ public IBASwitch(int numberOfInputPorts, int numberOfOutputPorts, int numberOfVirtualLanes, String ID, Kernel kernel){ this(kernel); int i; this.numberOfVirtualLanes = numberOfVirtualLanes; // Create input and output ports inputPorts = new IBAInputPort[numberOfInputPorts]; for (i=0; i < numberOfInputPorts; i++){ inputPorts[i] = new IBAInputPort(numberOfVirtualLanes, kernel); inputPorts[i].setSink(this); inputPorts[i].setID(ID+", input port "+i); } outputPorts = new IBAOutputPort[numberOfOutputPorts]; for (i=0; i < numberOfOutputPorts; i++){ outputPorts[i] = new IBAOutputPort(numberOfVirtualLanes, kernel); outputPorts[i].setID(ID+", output port "+i); } // Assign ID this.ID = ID; // Other initialization VLstates = new VLstate[inputPorts.length][numberOfVirtualLanes]; for (i=0; i < VLstates.length; i++){ for (int j=0; j < VLstates[i].length; j++){ VLstates[i][j] = new VLstate(); } 159 } random = new Random(1L); drawHistory = new int[inputPorts.length]; // prefs this.procTime = Integer.getInteger("infiniband.PACKET_PROCESSING_TIME").intValue(); this.sleepTime = Integer.getInteger("infiniband.SLEEP_TIME").intValue(); this.blockSize = Integer.getInteger("infiniband.BLOCK_SIZE").intValue(); } /** * Sets the routing table for this switch. */ public void setRoutingTable(IBARoutingTable routingTable){ this.routingTable = routingTable; } /** * Adds an address range that this source node can send * to. The format of the string can be one of the following, * where aaa, bbb, ccc and ddd represents integers between 0 and 255: * <table> * <tr><td>aaa.bbb</td><td>Adds the specified address</tr> * <tr><td>aaa.bbb-ccc.ddd</td><td>Adds addresses ranging from * aaa.bbb to ccc.ddd, inclusive.</td></tr> * </table> * @param range A string describing the address range. */ public void addRoutingInterval(String range, int outputPortNumber){ Address[] addrs = Utils.getAddresses(range); if (outputPortNumber < 0 || outputPortNumber > outputPorts.length){ throw new IllegalArgumentException("Output port number "+ "out of range. Must be"+ " between 0 and "+ (outputPorts.length-1)+ "."); } for (int i=0; i < addrs.length; i++){ routingTable.addRoute(addrs[i], outputPortNumber); // {{{ DEBUG /* Reporter.debugln(ID+": Sending address \""+addrs[i]+ "\" to port "+outputPortNumber); */ // }}} } } /** * Deletes an address range that this source node can send * to. The format of the string can be one of the following, * where aaa, bbb, ccc and ddd represents integers between 0 and 255: * <table> * <tr><td>aaa.bbb</td><td>Adds the specified address</tr> * <tr><td>aaa.bbb-ccc.ddd</td><td>Adds addresses ranging from * aaa.bbb to ccc.ddd, inclusive.</td></tr> * </table> * @param range A string describing the address range. 160 */ public void deleteRoutingInterval(String range, int outputPortNumber){ Address[] addrs = Utils.getAddresses(range); if (outputPortNumber < 0 || outputPortNumber > outputPorts.length){ throw new IllegalArgumentException("Output port number "+ "out of range. Must be"+ " between 0 and "+ (outputPorts.length-1)+ "."); } for (int i=0; i < addrs.length; i++){ routingTable.deleteRoute(addrs[i], outputPortNumber); // {{{ DEBUG /* Reporter.debugln(ID+": Not sending address \""+addrs[i]+ "\" to port "+outputPortNumber); */ // }}} } } /** * Attaches a link to this switch on the * supplied input port number */ public void attachInput(Link l, int inputPortNumber) throws IllegalArgumentException { if (inputPorts == null){ throw new IllegalArgumentException("This switch has no "+ "input ports."); } if (inputPortNumber < 0 || inputPortNumber > inputPorts.length){ throw new IllegalArgumentException("Input port number out "+ "of range. Must be"+ "between 0 and "+ inputPorts.length); } inputPorts[inputPortNumber].attach(l); // Update the ID to be able to distinguish // input ports from each other inputPorts[inputPortNumber].setID(ID+", input port "+ inputPortNumber); } /** * Gets the neighbor on the supplied output port */ public Scheduleable getOutNeighbour(int portNumber){ return ((IBAInputPort)outputPorts[portNumber].getSink()).getSink(); } /** * Gets the neighbor on the supplied output port */ public Scheduleable getInNeighbour(int portNumber){ return (Scheduleable) inputPorts[portNumber].getSource(); 161 } /** * Returns a pointer to the output ports */ public IBAOutputPort[] getOutputPorts(){ return outputPorts; } /** * Sets the number of units we can send from high priority * before letting a unit from low priority send. * @param portNumber The port number to set the limit for * @param limit The number of units */ public void setLimitOfHighPriority(int portNumber, int limit){ outputPorts[portNumber].setLimitOfHighPriority(limit); } /** * Adds an entry to the high priority list of this switch. * Means that port number <tt>portNumber</tt> is allowed to send * <tt>weight</tt> units on virtual lane number <tt>VLNumber</tt>. * This entry has high priority, which means that it will be selected * before any of the entries in the low priority table. */ public void addHighPriority(int portNumber, int VLNumber, int weight){ outputPorts[portNumber].addHighPriority(VLNumber, weight); } /** * Adds an entry to the low priority list of this switch. * Means that port number <tt>portNumber</tt> is allowed to send * <tt>weight</tt> units on virtual lane number <tt>VLNumber</tt>. * This entry has low priority, which means that it will be selected * after any of the entries in the high priority table. */ public void addLowPriority(int portNumber, int VLNumber, int weight){ outputPorts[portNumber].addLowPriority(VLNumber, weight); } /** * Attaches a link to this switch on the * supplied output port number */ public void attachOutput(Link l, int outputPortNumber) throws IllegalArgumentException { if (outputPorts == null){ throw new IllegalArgumentException("This switch has no "+ "output ports."); } if (outputPortNumber < 0 || outputPortNumber > outputPorts.length){ throw new IllegalArgumentException("Output port number out "+ "of range. Must be"+ "between 0 and "+ outputPorts.length); } outputPorts[outputPortNumber].attach(l); 162 // Update the ID to be able to distinguish // output ports from each other outputPorts[outputPortNumber].setID(ID+", output port "+ outputPortNumber); } protected void checkForData(VLstate state, int portNum, int vlNum){ // Reinitialize the state of this VL state.reset(); if (inputPorts[portNum].hasDataOnVL(vlNum)){ state.state = VLstate.RECEIVING_HEADER; state.numberOfBytesReceived = 0; receiveHeader(state, portNum, vlNum); } } protected void receiveHeader(VLstate state, int portNum, int vlNum){ /* // DEBUG if (ID.startsWith("Switch 0")){ Reporter.println("Time: "+kernel.currentTime()+", "+ID+": "+ "("+portNum+","+vlNum+") ,"+ "Receiving header."); } // */ int headerSize = IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE; // Read the header for (int k=0; k < headerSize; k++) { // Check for internal simulator errors if (!inputPorts[portNum].hasDataOnVL(vlNum)){ Reporter.error("IBASwitch.receiveHeader: No data in "+ "input port "+portNum+", VL "+vlNum); } state.lrh[k] = (inputPorts[portNum].receiveByte(vlNum)); state.numberOfBytesReceived++; } // If we have read a whole lrh, // start processing header. if (state.numberOfBytesReceived == headerSize ){ state.state = VLstate.PROCESSING_HEADER; state.remainingProcessingTime = procTime; kernel.schedule(this, kernel.currentTime() + 1); // Start processing of header processHeader(state, portNum, vlNum); }else{ Reporter.error("IBASwitch.receiveHeader: Coulnd’t read whole header. "+ "Read "+state.numberOfBytesReceived+" bytes, needed "+ headerSize); } } 163 protected void processHeader(VLstate state, int portNum, int vlNum){ /* // DEBUG if (ID.startsWith("Switch 0")){ Reporter.println("Time: "+kernel.currentTime()+", "+ID+": "+ "("+portNum+","+vlNum+") ,"+ "Processing header. Processing time left: " +(state.remainingProcessingTime)); } // */ IBAPacket.LRH lrh; // sanity check if (state.remainingProcessingTime < 0 ){ Reporter.error("IBASwtich.act: Remaining "+ "processing time is negative: "+ state.remainingProcessingTime); } if (state.remainingProcessingTime == 0){ lrh = dummyPacket.new LRH(state.lrh); // Find the output port and VL Route r = routingTable.lookUp(lrh); state.outputPort = r; // TODO: Do something sensible if we have no // routing table entry for that address. if (r == null){ throw new RuntimeException(ID+": "+ "No routing table"+ " entry for "+ "address "+ new Address(lrh.DLID()).toString()); } if (outputPorts[r.portNumber]. canSend(r.VLNumber, IBAPacketParser.packetLength(lrh)) && (!outputPorts[r.portNumber]. occupied(r.VLNumber)) ){ // // // // Tell the port that we want to send on that VL to avoid that any other packet is sent on the same VL on the same time. outputPorts[r.portNumber]. occupy(r.VLNumber); state.state = VLstate.VIRTUAL_CUT_THROUGH; state.numberOfBytesSent = 0; kernel.schedule(this, kernel.currentTime() + 1); // Start Virtual cut-through virtualCutThrough(state, portNum, vlNum); }else{ state.state = VLstate.WAITING_FOR_OUTPUT_LINK; state.numberOfBytesSent = 0; kernel.schedule(this, kernel.currentTime() + sleepTime); } 164 } else // if (state.remainingProcessingTime > 0) { // Read rest of a 64-byte block of data // Wait ’processingTime’ time units, and then perform // the whole lookup, instead of adding an ’act’ method // to the routing table. Same result, simpler code. state.remainingProcessingTime--; kernel.schedule(this, kernel.currentTime() + 1); } } protected void waitForOutputLink(VLstate state, int portNum, int vlNum){ IBAPacket.LRH lrh; lrh = dummyPacket.new LRH(state.lrh); // Find the output port and VL Route r = routingTable.lookUp(lrh); state.outputPort = r; // Check if the output port has space on that VL if (outputPorts[r.portNumber]. canSend(r.VLNumber, IBAPacketParser.packetLength(lrh)) && (!outputPorts[r.portNumber]. occupied(r.VLNumber)) ){ // Occupy the output VL outputPorts[r.portNumber]. occupy(r.VLNumber); state.state = VLstate.FORWARD; state.numberOfBytesSent = 0; // Start forwarding of the packet forward(state, portNum, vlNum); } else{ kernel.schedule(this, kernel.currentTime() + sleepTime); } } protected void virtualCutThrough(VLstate state, int portNum, int vlNum){ /* // DEBUG if (ID.startsWith("Switch 0")){ Reporter.println("Time: "+kernel.currentTime()+", "+ID+": "+ "("+portNum+","+vlNum+") ,"+ "Sending block "+(int)(Math.ceil( ((double)state.numberOfBytesReceived)/64))+ " on VL "+vlNum); } // */ forward(state, portNum, vlNum); } 165 /** * Sends a block onto the output ports of the switch. */ protected void sendBlock(VLstate state, int portNum, int vlNum, int numBytes) throws NotClearToSendException{ IBAPacket.LRH lrh; lrh = dummyPacket.new LRH(state.lrh); /* // DEBUG Reporter.debugln("IBAPacketParser.packetLength(lrh) = "+ IBAPacketParser.packetLength(lrh)); Reporter.debugln("state.numberOfBytesSent = "+ state.numberOfBytesSent); Reporter.debugln("(IBAPacketParser.packetLength(lrh) - "+ "state.numberOfBytesSent) = "+ (IBAPacketParser.packetLength(lrh) state.numberOfBytesSent)); Reporter.debugln("numBytes to send = "+numBytes); // */ // To check for concistency int limit = (IBAPacketParser.packetLength(lrh) - state.numberOfBytesSent); for (int k=0; k < numBytes; k++) { if (k >= limit || ! inputPorts[portNum].hasDataOnVL(vlNum)){ Reporter.error("IBASwitch.sendBlock: "+ "input port "+portNum+", VL "+vlNum+" is empty! Trying to send "+ numBytes+", sent "+k+"."); } // Read one byte from input port and // put on corresponding output port try{ outputPorts[state.outputPort.portNumber]. sendByte(inputPorts[portNum].receiveByte(vlNum), state.outputPort. VLNumber); }catch (NotClearToSendException nctse){ nctse.printStackTrace(Reporter.getOutputStream()); System.exit(1); } state.numberOfBytesReceived++; state.numberOfBytesSent++; // To know if we should schedule // the next unit or not bytesSentOnCurrentVLThisTurn++; // Check if we are done if (state.numberOfBytesSent == IBAPacketParser.packetLength(lrh) ){ // Release the VL outputPorts[state.outputPort. portNumber]. release(state.outputPort. VLNumber); state.state = VLstate.READY; 166 // // // // // Reschedule in case there’s more data immediately waiting. We could have "lost" some schedulings by the input port if we were already scheduled, so there could be more data waiting. kernel.schedule(this, kernel.currentTime() + 1); break; // out of foor loop } } } protected void forward(VLstate state, int portNum, int vlNum){ IBAPacket.LRH lrh; lrh = dummyPacket.new LRH(state.lrh); // Send the lrh first if (state.numberOfBytesSent == 0){ int numBytes = IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE; for (int k=0; k < numBytes; k++){ // Check for internal simulator errors if (!inputPorts[portNum].hasDataOnVL(vlNum)){ Reporter.error("IBASwitch.forward: No data in "+ "input port "+portNum+", VL "+vlNum); } try{ outputPorts[state.outputPort.portNumber].sendByte(state.lrh[k], state.outputPort. VLNumber); }catch (NotClearToSendException nctse){ nctse.printStackTrace(Reporter.getOutputStream()); System.exit(1); } state.numberOfBytesSent++; // To know if we should schedule // the next unit or not bytesSentOnCurrentVLThisTurn++; } // Send rest of a IBA_BLOCK_SIZE block of the // packet (64-8 = 56 bytes for the moment.) numBytes = blockSize(IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE); sendBlock(state, portNum, vlNum, numBytes); } // If we have sent the lrh, send a block // from the rest of the packet. else{ int numBytes = Math.min(blockSize, (IBAPacketParser.packetLength(lrh) state.numberOfBytesSent)); 167 sendBlock(state, portNum, vlNum, numBytes); } // Reschedule if we are not finished if (state.numberOfBytesSent != IBAPacketParser.packetLength(lrh) ){ kernel.schedule(this, kernel.currentTime() + 1); } /* // DEBUG if (ID.startsWith("Switch 0")){ Reporter.println("Time: "+kernel.currentTime()+", "+ID+": "+ "("+portNum+","+vlNum+"), "+ "Sending block "+(int)(Math.ceil( ((double)state.numberOfBytesReceived)/64))+ " ("+state.numberOfBytesSent+" bytes) on VL "+vlNum); } // */ } /** * Loops through all VLs on all input ports and processes data * on it. Resembles a fully parallellized swich that is able * to process data on all incoming interfaces simultaneously. * * See p. 853 in the IBA spec: The packets shall not * leave the input buffers if there’s not enough room in the output * buffers. * * C18-25: * Except for virtual lane 15, if the virtual lane on the * outbound port does not contain sufficient space for the packet to * be relayed, then the packet shall remain in the virtual lane on * the inbound port until sufficient space is available or until * the switch lifetime limit mechanism permits the discard of * the packet. * */ public void act(){ // dummy, just for jbuilder to be able to set breakpoint before // the for loop. int dust = 0; // // // // // // // // // // // Only loop through the input ports that have data. This is very important when the net is congested. If we loop through all input ports, and one of them is unused, the input port that is numbered one above the unused port gets the empty port’s chance to go first in addition to its own. This results in an uneven distribution of the capacity between the input ports, because when the buffers on the output ports are nearly full, it’s crucial for an input port to get to try first to be allowed to send data, since the first one to try will occupy the output port, and hence the other input ports will not have a chance to send. 168 // // The solution is to first check which input ports have data to send, // and then only loop through these input ports, starting on a random port // each time. // Find out how many output ports have data to send int numWithData = 0; for (int i=0; i < inputPorts.length; i++) { for (int j=0; j < inputPorts[i].numberOfVLs(); j++) { if (inputPorts[i].hasDataOnVL(j)){ numWithData++; // don’t count an input port with data on more // than one VL more than once. break; } } } int[] inputPortsWithData = new int[numWithData]; for (int i=0,k=0; i < inputPorts.length; i++) { for (int j=0; j < inputPorts[i].numberOfVLs(); j++) { if (inputPorts[i].hasDataOnVL(j)){ inputPortsWithData[k++] = i; // don’t add an input port with data on more // than one VL more than once. break; } } } /* // Loop through all ports, starting on a new one each time. for (int i=inputPortNumberToStartOn, n=0; n < inputPorts.length; i = (i+1) % inputPorts.length, n++ ){ */ // Return if there’s no data to send. if (numWithData == 0) return; // // // // // // // Randomize order of input ports in the list of ports with data to send, to avoid that e.g. port 3 gets to send before port 0 both when port 1, 2 and 3 gets to try first. In all these cases, 3 will have a go before 0, and will be able to send more times than port 0. Serving ports equally is not easy. // Copy list of input port numbers to a dynamic array ArrayList tmp = new ArrayList(inputPortsWithData.length); for (int i=0; i < inputPortsWithData.length; i++) { tmp.add(new Integer(inputPortsWithData[i])); } // Add all items randomly to the original array, // and then the elements should be reordered randomly. for (int i=0; i < inputPortsWithData.length; i++) { inputPortsWithData[i] = ((Integer)tmp.remove(random. nextInt(tmp.size()))).intValue(); } // Loop through ports with data waiting to send 169 // No need to randomize the index to start on when // the order of the ports in the array is random. // This will perhaps introduce less randomness. //int indexToStartOn = random.nextInt(numWithData); int indexToStartOn = 0; drawHistory[inputPortsWithData[indexToStartOn]]++; for (int i=indexToStartOn, n=0; n < numWithData; i = (i+1) % numWithData, n++) { for (int j=0; j < inputPorts[inputPortsWithData[i]].numberOfVLs(); j++){ bytesSentOnCurrentVLThisTurn = 0; VLstate state = VLstates[inputPortsWithData[i]][j]; switch(state.state){ case(VLstate.READY): checkForData(state, inputPortsWithData[i], j); break; case(VLstate.RECEIVING_HEADER): receiveHeader(state, inputPortsWithData[i], j); break; case(VLstate.PROCESSING_HEADER): processHeader(state, inputPortsWithData[i], j); break; case(VLstate.WAITING_FOR_OUTPUT_LINK): waitForOutputLink(state, inputPortsWithData[i], j); break; case(VLstate.VIRTUAL_CUT_THROUGH): virtualCutThrough(state, inputPortsWithData[i], j); break; case(VLstate.FORWARD): forward(state, inputPortsWithData[i], j); break; default: Reporter.error("IBASwitch.act: Illegal input VL "+ "state: "+state.state); } // Schedule the output port so that the packet moves on // if any bytes have been sent. if (bytesSentOnCurrentVLThisTurn != 0){ /* // DEBUG if (ID.startsWith("Switch 0")){ Reporter.println("Time: "+kernel.currentTime()+", "+ID+": "+ "Scheduling output port "+ outputPorts[state. outputPort.portNumber]+ " on time "+(kernel.currentTime() + 1)+"."); } // */ 170 kernel.schedule(outputPorts[state. outputPort.portNumber], kernel.currentTime() + 1); } } } // Update which input port number we should start to check for // incoming data the next time we are called. /* inputPortNumberToStartOn = (inputPortNumberToStartOn + 1) % inputPorts.length; */ // Always serve packets waiting for output links first, // to avoid filling up with a new packet from another input link // on the next time step, and hence starving the flow waiting for // the output port forever. // // If more than one flow is waiting for the output link, eventually // all flows should be served, since on the next time step, // the one that got prioritized this time won’t be waiting for the // output link anymore, and then the next flow waiting will be served, // and so on until all streams should be served. /* boolean changed = false; for (int i=0; i < inputPorts.length && !changed; i++) { for (int j=0; j < inputPorts[i].numberOfVLs(); j++) { VLstate state = VLstates[i][j]; if (state.state == VLstate.WAITING_FOR_OUTPUT_LINK) { inputPortNumberToStartOn = i; // Stop iteration changed = true; } } } if (!changed || (changed && random.nextInt(2)==0)){ // Try random value each time inputPortNumberToStartOn = random.nextInt(inputPorts.length); drawHistory[inputPortNumberToStartOn]++; } */ /* inputPortNumberToStartOn = random.nextInt(inputPorts.length); drawHistory[inputPortNumberToStartOn]++; */ } /** * Returns the number of packets sent through the switch. */ public int[] getTotalNumberOfPacketsSent() { int[] total = new int[outputPorts.length]; for (int i=0; i < outputPorts.length; i++) { /* for (int j=0; j< numberOfVirtualLanes; j++) { 171 total[i] += outputPorts[i].getTotalNumberOfPacketsSent(j); } */ total[i] = outputPorts[i].getTotalNumberOfPacketsSent(); } return total; } /** * Returns the random draw history */ public int[] getDrawHistory() { return drawHistory; } public String report(){ return "IBASwitch \""+ID+"\"."; } /** * The input ports of this switch */ protected IBAInputPort[] inputPorts; /** * The output ports of this switch */ protected IBAOutputPort[] outputPorts; /** * The number of virtual lanes (VLs) supported by this switch */ protected int numberOfVirtualLanes; /** * The buffer size of the input buffers */ protected int inputBufferSize; /** * The buffer size of the output buffers */ protected int outputBufferSize; /** * The routing table to look up addresses in */ protected IBARoutingTable routingTable; /** Dummy packet used to create IBAPacket.LRH objects */ protected static final IBAPacket dummyPacket = new IBAPacket(); /** * Which input port to start looking for new packets on * when act() is called. Should start on a new one each time, * to ensure fairness. */ protected int inputPortNumberToStartOn = 0; /** To know if we should schedule the next unit or not */ protected int bytesSentOnCurrentVLThisTurn = 0; 172 /** * To draw next input port to start checking for incoming traffic * on. */ protected Random random; /** * To keep track of random draws */ protected int[] drawHistory; // These are here to avoid reading system preferences all the time. // I think that is rather inefficient. /** * The time it takes to process a packet in the switch */ protected int procTime; /** * The time to sleep if there’s no space available to send data */ protected int sleepTime; /** * The size of a block */ protected int blockSize; /** * The state of all the input VLs */ protected VLstate[][] VLstates; protected class VLstate{ public int state; public Route outputPort; public byte[] lrh; public int remainingProcessingTime; public int numberOfBytesReceived; public int numberOfBytesSent; protected VLstate(){ init(); } protected void init(){ state = READY; outputPort = null; lrh = new byte[IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE]; remainingProcessingTime = -1; numberOfBytesReceived = -1; numberOfBytesSent = -1; } protected void reset(){ init(); } public public public public public public static static static static static static final final final final final final int int int int int int READY RECEIVING_HEADER PROCESSING_HEADER VIRTUAL_CUT_THROUGH WAITING_FOR_OUTPUT_LINK FORWARD 173 = = = = = = 0; 1; 2; 3; 4; 5; } } 174 no.uio.ifi.gruns.infiniband.IBAPacketParser package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; public class IBAPacketParser{ /** * Returns the total length of the packet * with the supplied LRH, including all CRCs and padding */ public static int packetLength(IBAPacket.LRH header){ int pktLen; // +2 is for the VCRC, which is not included in PktLen. pktLen = (header.PktLen() * 4)+ 2; return pktLen; } /** * Returns the total length of the packet * with the supplied LRH bytes. */ public static int packetLength(byte[] lrh){ return packetLength(dummyPacket.new LRH(lrh)); } /** * Returns the next LRH from the supplied ByteBuffer. * The header is removed from the buffer. */ public static IBAPacket.LRH getNextHeader(ByteBuffer buffer){ IBAPacket.LRH lrh; byte[] tmpByte = new byte[IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE]; for (int i=0; i < tmpByte.length; i++){ tmpByte[i] = buffer.get(); } lrh = dummyPacket.new LRH(tmpByte); return lrh; } /** * Extracts the first IBA packet from the supplied * {@link no.uio.ifi.gruns.ByteBuffer} and returns it. * If you have a partially filled packet and want to * fill it up from the ByteBuffer, supply the packet in * the second parameter. * Otherwise, send in an empty IBAPacket. * * @param bytestream The stream of bytes to read from * @param packet The packet to fill up 175 * @return true when the packet is filled up, false if there was * not enough data to fill the packet. */ public static boolean getNextPacket(ByteBuffer bytestream, IBAPacket packet){ byte[] tmpByte = null; int tmpLen = 0; // Read in the LRH from the byte stream if this is the // first time called (e.g. packet.getSize() == 0) if (packet.getSize() == 0){ // Update number of bytes read bytesRead = 0; // LRH // not enough bytes to read the whole LRH yet. if (bytestream.getSize() < IBAPacket.LRH_SIZE){ return false; } tmpLen = IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.lrh = packet.new LRH(tmpByte); } // // // if If not all of the packet has arrived yet, return false. TODO: Maybe change this later to return true when the LRH can be read. Or not. Think it over. (bytestream.getSize() < packet.getSize() - bytesRead){ return false; } // GRH if (packet.grh.isPresent()){ tmpLen = IBAPacket.GRH_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.grh = packet.new GRH(tmpByte); } // BTH if (packet.bth.isPresent()){ tmpLen = IBAPacket.BTH_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.bth = packet.new BTH(tmpByte); } // RDETH 176 if (packet.rdeth.isPresent()){ tmpLen = IBAPacket.RDETH_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.rdeth = packet.new RDETH(tmpByte); } // DETH if (packet.deth.isPresent()){ tmpLen = IBAPacket.DETH_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.deth = packet.new DETH(tmpByte); } // RETH if (packet.reth.isPresent()){ tmpLen = IBAPacket.RETH_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.reth = packet.new RETH(tmpByte); } // AtomicETH if (packet.atomicETH.isPresent()){ tmpLen = IBAPacket.ATOMIC_ETH_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.atomicETH = packet.new AtomicETH(tmpByte); } // AETH if (packet.aeth.isPresent()){ tmpLen = IBAPacket.AETH_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.aeth = packet.new AETH(tmpByte); } // AtomicAckETH if (packet.atomicAckETH.isPresent()){ tmpLen = IBAPacket.ATOMIC_ACK_ETH_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; 177 } packet.atomicAckETH = packet.new AtomicAckETH(tmpByte); } // ImmDt if (packet.immDt.isPresent()){ tmpLen = IBAPacket.IMM_DT_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.immDt = packet.new ImmDt(tmpByte); } // PYLD if (packet.pyld.isPresent()){ tmpLen = packet.getSize() - bytesRead - ( (IBAPacket.ICRC_SIZE + IBAPacket.VCRC_SIZE) / IBAPacket.BITS_PER_BYTE); tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.pyld = packet.new PYLD(tmpByte); } // ICRC if (packet.icrc.isPresent()){ tmpLen = IBAPacket.ICRC_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.icrc = packet.new ICRC(tmpByte); } // VCRC if (packet.vcrc.isPresent()){ tmpLen = IBAPacket.VCRC_SIZE / IBAPacket.BITS_PER_BYTE; tmpByte = new byte[tmpLen]; for (int i=0; i < tmpLen; i++){ tmpByte[i] = bytestream.get(); bytesRead++; } packet.vcrc = packet.new VCRC(tmpByte); } return true; } /** Specicies the number of bytes read of the current packet. */ protected static int bytesRead = 0; /** * Used to avoid creating lots of useless IBAPacket objects in * getNextHeader() 178 */ protected static final IBAPacket dummyPacket = new IBAPacket(); } 179 no.uio.ifi.gruns.infiniband.IBAStatistics package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; import java.util.HashMap; import java.util.Iterator; public class IBAStatistics { /** * Indicates when the transient period is over, * so we should start gathering data. */ protected static int transientThreshold; /** * Indicates that the transient period is over */ protected static boolean transientPeriod; public static HashMap droppedPackets; public static HashMap drainNodes; public static HashMap switchPriorities; public static Kernel kernel; static{ droppedPackets = new HashMap(); drainNodes = new HashMap(); switchPriorities = new HashMap(); transientThreshold = Integer.getInteger("infiniband.TRANSIENT_PERIOD", 0).intValue(); transientPeriod = true; } /** * Sets the kernel. Needed to check if the transient period is * over. */ public static void setKernel(Kernel k){ kernel = k; } public static void reportDroppedPacket(IBASourceNode node){ int numPackets = 0; if (droppedPackets.containsKey(node)){ numPackets = ((Integer) droppedPackets.get(node)).intValue(); } numPackets++; droppedPackets.put(node, new Integer(numPackets)); } public static HashMap getDroppedPacketStats(){ return droppedPackets; } public static int getDroppedPacketStats(IBASourceNode node){ if (droppedPackets.containsKey(node)){ return ((Integer)droppedPackets.get(node)).intValue(); } return 0; } 180 public static void reportReceivedPacket(IBADrainNode node, int portNumber, int VLNumber, IBAPacket.LRH lrh, int numBytes){ IBADrainNodeStats stats; if (transientPeriod) { if (kernel.currentTime() > transientThreshold) { transientPeriod = false; } }else { if (drainNodes.containsKey(node)){ stats = (IBADrainNodeStats) drainNodes.get(node); }else{ stats = new IBADrainNodeStats(node); drainNodes.put(node, stats); } stats.increaseReceivedPackets(lrh, portNumber, VLNumber); stats.increaseReceivedBytes(portNumber, VLNumber, numBytes); drainNodes.put(node, stats); } } public static IBADrainNodeStats getReceivedPacketStats(IBADrainNode node){ return (IBADrainNodeStats) drainNodes.get(node); } public static void reportPriorities(IBAOutputPort ibao, int hiPri, int loPri){ Double prevAvgObj; double prevAvg = 0; double newAvg; if (transientPeriod) { if (kernel.currentTime() > transientThreshold) { transientPeriod = false; } }else { if (switchPriorities.containsKey(ibao)){ prevAvgObj = (Double) switchPriorities.get(ibao); prevAvg = prevAvgObj.doubleValue(); } if (loPri > 0){ newAvg = ((double)hiPri) / loPri; } else { newAvg = -42.0; } // Better not to keep the old avg., because // The errors in the beginning affects the overall avg. // switchPriorities.put(ibao, new Double((prevAvg + newAvg) / 2)); switchPriorities.put(ibao, new Double(newAvg)); } } public static double getPriorities(IBAOutputPort ibao){ double retVal = -43; // No stats if (switchPriorities.containsKey(ibao)){ retVal = ((Double) switchPriorities.get(ibao)).doubleValue(); } 181 return retVal; } public static double getAverageHiPriRatio(){ double retVal = 0; Iterator it = switchPriorities.values().iterator(); while (it.hasNext()){ retVal += ((Double)it.next()).doubleValue(); } retVal /= switchPriorities.size(); return retVal; } public static class IBADrainNodeStats{ public IBADrainNodeStats(IBADrainNode node){ totalNumberOfPacketsReceived = new int[node.getNumberOfInputPorts()][node.getNumberOfVLs()]; totalNumberOfBytesReceived = new int[node.getNumberOfInputPorts()][node.getNumberOfVLs()]; packetsReceivedFromEachSourceNode = new HashMap(); packetsReceivedOnEachSL = new int[Integer. parseInt(System. getProperty("infiniband.MAX_NUMBER_OF_VLS"))]; } public int getTotalNumberOfReceivedPackets(){ int total = 0; for (int i=0; i < totalNumberOfPacketsReceived.length; i++){ for (int j = 0; j < totalNumberOfPacketsReceived[i].length; j++){ total += totalNumberOfPacketsReceived[i][j]; } } return total; } public int getTotalNumberOfReceivedBytes(){ int total = 0; for (int i=0; i < totalNumberOfBytesReceived.length; i++){ for (int j = 0; j < totalNumberOfBytesReceived[i].length; j++){ total += totalNumberOfBytesReceived[i][j]; } } return total; } public HashMap getPacketsReceivedPerSource(){ return packetsReceivedFromEachSourceNode; } private void increaseReceivedPackets(IBAPacket.LRH lrh, int portNumber, int VLNumber){ int numPkts=0; String SLID = ""+new Address(lrh.SLID()); // per-source stats if (packetsReceivedFromEachSourceNode. containsKey(SLID)){ numPkts = ((Integer) packetsReceivedFromEachSourceNode. remove(SLID)). intValue(); } packetsReceivedFromEachSourceNode. put(SLID, 182 new Integer(numPkts + 1)); // per-(port,VL)-stats totalNumberOfPacketsReceived[portNumber][VLNumber]++; // per-SL-stats packetsReceivedOnEachSL[lrh.SL()]++; } public void increaseReceivedBytes(int portNumber, int VLNumber, int numBytes){ totalNumberOfBytesReceived[portNumber][VLNumber]+=numBytes; } /** The total number of received packets per VL per input port */ private int [][] totalNumberOfPacketsReceived; /** The total number of received bytes per input VL */ private int [][] totalNumberOfBytesReceived; /** The total number of packets received from each source node. */ private HashMap packetsReceivedFromEachSourceNode; /** The total number of packets received on each service level */ private int[] packetsReceivedOnEachSL; } } 183 no.uio.ifi.gruns.infiniband.IBAOutputPort package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; /** * The output ports on IBA units */ public class IBAOutputPort extends OutputPort{ /** * @param numberOfVirtualLanes The number of virtual lanes * @param bufferSize The size of the output buffers * @param kernel The kernel to use for simulation */ public IBAOutputPort(int numberOfVirtualLanes, int bufferSize, Kernel kernel) { super(kernel); this.numberOfVLs = numberOfVirtualLanes; this.bufferSize = bufferSize; init(); } /** * Uses bufferSize = 16 * Integer.parseInt * (System.getProperty("infiniband.EXP_PACKET_SIZE")); * * @param numberOfVirtualLanes The number of virtual lanes * @param kernel The kernel to use for simulation */ public IBAOutputPort(int numberOfVirtualLanes, Kernel kernel){ super(kernel); this.numberOfVLs = numberOfVirtualLanes; // Do this initialization here instead of superclass // to control the size of the buffers // bufferSize = 4 * IBAPacket.MAX_SIZE; // Room for 16 packets on each outgoing buffer bufferSize = 4* Integer.parseInt(System.getProperty("infiniband.REAL_PACKET_SIZE")); // Just to keep track of things System.setProperty("infiniband.OUTPUT_PORT_BUFSIZE", init(); } /** * Common initialization */ protected void init(){ this.buffer = new ByteBuffer[numberOfVLs]; this.VLstates = new VLstate[numberOfVLs]; for (int i=0; i < numberOfVLs; i++){ buffer[i] = new ByteBuffer(bufferSize); VLstates[i] = new VLstate(); } // Default "uninitialized" value ""; ID = bytesTransferred = 0; 184 ""+bufferSize); highPriority = new ListEntry[64]; lowPriority = new ListEntry[64]; // // // // Default to no limit on how many packets to send on high priority before the low priority queue get a shot. Means the low priority queue could starve. limitOfHighPriority = 255; sendingLRHs = new IBAPacket.LRH[numberOfVLs]; FCTBS = new int [numberOfVLs]; sendingState = READY; lrhIsSent = false; transientThreshold = Integer.getInteger("infiniband.TRANSIENT_PERIOD", 0).intValue(); transientPeriod = true; totalNumberOfPacketsSent=0; } /** * Sends a single packet on the supplied virtual lane number. * Called from the source to send a packet. Buffers the packet * on this outgoing port. */ public void sendIBAPacket(IBAPacket p, int VLNumber) throws NotClearToSendException{ byte[] content = p.getContent(); int i; for (i = 0; i < content.length && !buffer[VLNumber].isFull(); i++){ buffer[VLNumber].put(content[i]); } // Error message if error sending packet if (i < p.getSize()){ Reporter.error("IBAOutputPort.sendIBAPacket("+p+ ", "+VLNumber+"): "+ "Not enough buffer space left!"); Reporter.error("\n"); } } /** * Sends a single byte on the supplied virtual lane number */ public void sendByte(byte b, int VLNumber) throws NotClearToSendException{ // Update the state information VLstates[VLNumber].numberOfBytesReceived++; switch(VLstates[VLNumber].state){ case(VLstate.READY): throw new NotClearToSendException("Not Clear to send! "+ "Status = VLstate.READY."); case(VLstate.CLEAR_TO_SEND): 185 VLstates[VLNumber].state = VLstate.RECEIVING_HEAD; // fallthrough case(VLstate.RECEIVING_HEAD): buffer[VLNumber].put(b); // put the lrh in a separate array int index = VLstates[VLNumber]. numberOfBytesReceived - 1; VLstates[VLNumber].lrh[index] = b; if (VLstates[VLNumber].numberOfBytesReceived == IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE){ VLstates[VLNumber].state = VLstate.RECEIVING_PACKET; } break; case(VLstate.RECEIVING_PACKET): buffer[VLNumber].put(b); // Check if we have read the whole packet if (VLstates[VLNumber].numberOfBytesReceived == IBAPacketParser. packetLength(VLstates[VLNumber].lrh)){ VLstates[VLNumber].reset(); } break; default: Reporter.error("IBAOutputPort \""+ID+ "\".sendByte: Illegal state: "+ VLstates[VLNumber].state); } } /** * For logging purposes. Reports the total number of packets * sent from this output port on each virtual lane. * @param vl The virtual lane to get info on */ public int getTotalNumberOfPacketsSent(int vl){ return VLstates[vl].totalNumberOfPacketsSent; } /** * Reports the total number of packets sent all in all * on this output port. */ public int getTotalNumberOfPacketsSent(){ return this.totalNumberOfPacketsSent; } /** * Sends a number of bytes from the supplied VL out on the link */ protected void sendBytes(int VLNumber, int numBytes){ int i; for (i=0; i < numBytes; i++){ // Check for internal errors if (buffer[VLNumber].isEmpty()){ Reporter.error("IBAOutputPort.sendBytes: "+ "Buffer is empty! Trying to send "+ numBytes+", sent "+i+"."); } 186 link.sendByte(buffer[VLNumber].get()); } } /** * Sends a block from the current packet on the link */ protected void sendBlockFromCurrent(){ // To avoid scheduling the sink without having put data on it, // we record how many bytes we have sent. int bytesSentThisTurn = 0; // Sizes of the packet with and without lrh int packetSize = IBAPacketParser. packetLength(sendingLRHs[current.VLNumber]); int contentSize = packetSize (IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE); int lrhSize = (IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE); // Send header and a smaller block if header is not sent if (!lrhIsSent){ // send header for (int i=0; i < lrhSize ; i++, bytesTransferred++){ link.sendByte(sendingLRHs[current.VLNumber].content[i]); // To avoid scheduling the sink without having // put data on it. bytesSentThisTurn++; } // Update the bytes in transit on the input port receiving. ((IBAInputPort) link.getSink()). increaseBytesSent(lrhSize, current.VLNumber); // Send a (Integer.parseInt(System.getProperty( // "infiniband.BLOCK_SIZE")) - sendingLRHsize)// byte block of the packet on the link. int numBytes = Math.min((Integer.parseInt( System.getProperty("infiniband.BLOCK_SIZE")) - lrhSize), packetSize - bytesTransferred); sendBytes(current.VLNumber, numBytes); bytesTransferred+=numBytes; // To avoid scheduling the sink without having // put data on it. bytesSentThisTurn+=numBytes; // Update the bytes in transit on the input port // receiving. ((IBAInputPort) link.getSink()). increaseBytesSent(numBytes, current.VLNumber); lrhIsSent = true; } // Send a block of the packet itself else{ // Send a Integer.parseInt(System.getProperty( // "infiniband.BLOCK_SIZE"))-byte block // of the packet on the link. int numBytes = Math.min(Integer.parseInt( System.getProperty("infiniband.BLOCK_SIZE")), packetSize - bytesTransferred); /* // DEBUG Reporter.debugln(ID+ ": IBAOutputPort.sendBlockFromCurrent: packetSize = "+packetSize); Reporter.debugln(ID+ 187 ": IBAOutputPort.sendBlockFromCurrent: bytesTransferred = "+ bytesTransferred); // */ sendBytes(current.VLNumber, numBytes); bytesTransferred+=numBytes; // To avoid scheduling the sink without having // put data on it. bytesSentThisTurn+=numBytes; // Update the bytes in transit on the input // port receiving. ((IBAInputPort) link.getSink()). increaseBytesSent(numBytes,current.VLNumber); } // Update the FCTBS for the current VL FCTBS[current.VLNumber]++; // Update the number of units sent on the current VL unitsSentOnCurrentVL++; // Update the number of units sent on high priority // if we are sending from high priority if (active == highPriority){ bytesSentOnHighPriority += bytesSentThisTurn; } // Schedule the link since we have put data on it if (bytesSentThisTurn !=0){ link.addBlock(bytesSentThisTurn); kernel.schedule(link, kernel.currentTime() + 1); } } /** * Indicates whether it is possible to send a packet according * to the list entry supplied. Returns true if one of the * following criteria is true: *<UL> * <LI><tt>entry.</LI *</UL> */ protected boolean allowedToSendFrom(ListEntry entry, int unitsSentOnVL){ boolean enoughCreditsAvailable = false; // // // // // // // // // // if Check if we can send data, i.e. if there are enough credits available on the receiving side. FCTBS = Flow Control Total Blocks Sent FCCL = Flow Control Credit Limit CR = Total blocks sent since link initialization plus the number of blocks in the data packet to be transmitted, all modulo 4096. CL = The last FCCL received in a flow control packet Exception for VL 15, which should always be sent. (entry==null || buffer[entry.VLNumber].isEmpty()){ enoughCreditsAvailable = false; } else if (entry.VLNumber == 15){ enoughCreditsAvailable = true; } else{ // Read the lrh for this output buffer if it’s not 188 // already read if (sendingLRHs[entry.VLNumber] == null){ sendingLRHs[entry.VLNumber] = IBAPacketParser.getNextHeader(buffer[entry.VLNumber]); } int CL = ((IBAInputPort) link.getSink()). getFCCL(FCTBS[entry.VLNumber], entry.VLNumber); int CR = (FCTBS[entry.VLNumber] + (int) Math.ceil( IBAPacketParser. packetLength(sendingLRHs[entry. VLNumber]) / Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE")))) % 4096; // // // // // enoughCreditsAvailable = ((CL - CR) % 4096 <= 2048); Must & the result with 0xfff to simulate a real 12 bit number, not just modulo 4096 on a 32-bit integer. The bits should always be interpreted as a number between 0 and 4095. enoughCreditsAvailable = ( (((CL - CR) % 4096) & 0xFFF) <= 2048); } return (entry.weight - unitsSentOnVL > 0 && !buffer[entry.VLNumber].isEmpty() && enoughCreditsAvailable); } protected ListEntry findNextListEntry(ListEntry[] queue, int current){ ListEntry le; int i; boolean found = false; // Check if we can continue sending on the // same VL, if not, reset the unitsSentOnCurrentVL. if (queue[current] != null && allowedToSendFrom(queue[current], unitsSentOnCurrentVL)){ return queue[current]; } // Loop through the list entries and find one // to send from. i = current + 1; for (int j=0; j < queue.length; j++){ le = queue[i]; if (le != null && allowedToSendFrom(le, 0)){ found = true; break; } i = (i+1) % queue.length; } if (found){ if (queue == highPriority){ currentHighPriority = i; }else if (queue == lowPriority){ currentLowPriority = i; }else{ // sanity check Reporter.error("IBAOutputPort.findNextListEntry: } unitsSentOnCurrentVL = 0; return (queue[i]); }else{ return null; } 189 Unknown queue supplied!"); } /** * Updates which VL we should send from, makes all decisions about * if the limit of high priority has run out, if we are allowed to * send from a particular VL etc. Called from act(). */ protected void updateCurrent(){ ListEntry le; // Reset sendingStates lrhIsSent = false; bytesTransferred = 0; // Check if we have exceeded the limit of high priority // packets. If we have, check the low priority queue first. if ( // limit == 255 means no limit limitOfHighPriority < 255 && ( // limit == 0 means we could send one packet. (limitOfHighPriority == 0 && bytesSentOnHighPriority > (limitOfHighPriority * 4096)) || (limitOfHighPriority > 0 && bytesSentOnHighPriority >= (limitOfHighPriority * 4096)) ) ) { bytesSentOnHighPriority = 0; le = findNextListEntry(lowPriority, currentLowPriority); if (le != null){ // Packets to send on Low Priority active = lowPriority; sendingState = SENDING; }else{ le = findNextListEntry(highPriority, currentHighPriority); if (le != null){// Packets to send on High Priority active = highPriority; sendingState = SENDING; } } } // If not, check the high priority first (usual case) else{ // Find next to send from le = findNextListEntry(highPriority, currentHighPriority); if (le != null){// Packets to send on High Priority active = highPriority; sendingState = SENDING; } else { // No packets to send on High Priority bytesSentOnHighPriority = 0; le = findNextListEntry(lowPriority, currentLowPriority); if (le != null){ // Packets to send on Low Priority active = lowPriority; bytesSentOnHighPriority = 0; sendingState = SENDING; } } } // Update the current list entry. this.current = le; 190 // Finished if nothing to send if (current == null){ // Reschedule if we still have data to send // but are not able to send right now for (int i=0; i < numberOfVLs; i++){ if (!buffer[i].isEmpty()){ kernel.schedule(this, kernel.currentTime() + 1); break; } } }else{ // Send one block of data sending(); } /* // DEBUG //if (ID.startsWith("Switch 0") //|| ID.startsWith("Switch 6") //|| ID.startsWith("Switch 7") //) { System.out.println("Time: "+kernel.currentTime()+", " +ID+": Selected VL "+current.VLNumber+". "+ "VL 1’s buffer is "+ (buffer[1].isEmpty()?"":"NOT ")+ "empty." ); //} // */ } /** * Sends a block from the current VL and checks if we are finished sending * the current packet. Reschedules if we are noe finished. */ protected void sending(){ // TODO: Small problem: When we are scheduled, and // have already read all the bytes in the packet, we should // start on the next packet, as of now, we only reset the state, // and don’t transfer any bytes. // // Shouldn’t happen, really, because when we have finished reading // all bytes, the state is reset, and we proceed to the next packet. Maybe // it’s not happening. Find out! sendBlockFromCurrent(); // Reschedule if not finished with current packet if (bytesTransferred < IBAPacketParser.packetLength(sendingLRHs[current. VLNumber])){ kernel.schedule(this, kernel.currentTime() + 1); } // Clear the lrh from this VL if we are finished // sending from it. Reset other parameters else if (bytesTransferred == IBAPacketParser.packetLength(sendingLRHs[current. VLNumber])){ sendingLRHs[current.VLNumber] = null; 191 sendingState = READY; // Logging if (transientPeriod) { if (kernel.currentTime() > transientThreshold) { transientPeriod = false; } }else { totalNumberOfPacketsSent++; VLstates[current.VLNumber].totalNumberOfPacketsSent++; int totLo=0, totHi=0; double ratio; for (int i=0; i < lowPriority.length; i++){ if (lowPriority[i] != null){ totLo += VLstates[lowPriority[i].VLNumber].totalNumberOfPacketsSent; } } for (int i=0; i < highPriority.length; i++){ if (highPriority[i] != null){ totHi += VLstates[highPriority[i].VLNumber].totalNumberOfPacketsSent; } } IBAStatistics.reportPriorities(this, totHi, totLo); ratio = IBAStatistics.getPriorities(this); } // Reschedule if we still have data to send // but are not able to send right now for (int i=0; i < numberOfVLs; i++){ if (!buffer[i].isEmpty()){ kernel.schedule(this, kernel.currentTime() + 1); break; } } } // Sanity check else if (bytesTransferred > IBAPacketParser.packetLength(sendingLRHs[current. VLNumber])){ Reporter.error("IBAOutputPort.act: Too many bytes sent! bytesTransferred +" > " + IBAPacketParser. packetLength(sendingLRHs[current. VLNumber])+"."); "+ } // Insanity check ;) else{ throw new java.lang.RuntimeException("Serious error! "+ "Should be "+ "impossible!"); } } /** * Keeps track of the limit of high priority, finds the next VL to send from * and sends one block of data on that VL. */ public void act(){ switch(sendingState){ 192 case READY: // Update which VL we should send from updateCurrent(); break; case SENDING: // Send a block of data and check if we are finished, etc. sending(); break; default: Reporter.error("IBAOutputPort.act: Invalid sendingState: "+sendingState); } } /** * Indicates whether this output port has any data to send * on a given virtual lane. */ public boolean dataOnVL(int VLNumber){ return !buffer[VLNumber].isEmpty(); } /** * Returns the status of the VL number supplied. * One of the following: * <ul> * <li>VLstate.READY</li> * <li>VLstate.RECEIVING_HEAD</li> * <li>VLstate.RECEIVING_PACKET</li> * </ul> */ public int getStatus(int VLNumber){ return VLstates[VLNumber].state; } /** * Occupies the supplied VLNumber, * i.e. tells others that we are currently transferring on this VL. */ public void occupy(int VLNumber){ VLstates[VLNumber].state = VLstate.CLEAR_TO_SEND; } /** * Indicates whether the supplied VL is occupied */ public boolean occupied(int VLNumber){ return VLstates[VLNumber].state != VLstate.READY; } /** * Releases the supplied VLNumber, * i.e. tells others that we are not transferring on this VL anymore. */ public void release(int VLNumber){ VLstates[VLNumber].state = VLstate.READY; } /** * Indicates to a unit that wants to send on this output port 193 * if we are able to send that number of bytes. * It returns whether we have the available buffer space to * accept the bytes, not if the unit in the other end has enough * room to receive them. * Returns false if there’s another packet currently coming in on * this VL. */ public boolean canSend(int VLNumber, int numberOfBytes){ boolean retVal = ((buffer[VLNumber].getCapacity() buffer[VLNumber].getSize()) >= numberOfBytes) && !occupied(VLNumber); return retVal; } /** * Returns the remaining buffer capacity on the supplied VL number */ public int remainingBufferCapacity(int VLNumber){ return (buffer[VLNumber].getCapacity() buffer[VLNumber].getSize()); } /** * Used to add a high or low priority entry. * @param queue Either @link{#highPriority# or @link{#lowPriority} * For more info, see @link{#addHighPriority} and @link{#addLowPriority}. */ protected void addEntry(int VLNumber, int weight, ListEntry[] queue){ ListEntry le; int i=0; for (i=0; i < queue.length; i++){ if (queue[i] == null){ le = new ListEntry(); le.VLNumber = VLNumber; le.weight = weight; queue[i] = le; break; } } // Error if no vacant slots if (i == queue.length){ Reporter.error("IBAOutputPort.addEntry("+ VLNumber+", "+weight+"): "+ "No vacant slots."); } } /** * Sets the high priority queue of this output port */ public void setHighPriotity(ListEntry[] queue){ this.highPriority = queue; } /** * Sets the number of units we can send from high priority * before letting a unit from low priority send. */ public void setLimitOfHighPriority(int limit){ limitOfHighPriority = limit; } 194 /** * Adds an entry of high priority on this output port. * @param VLNumber The Virtual lane to add an entry for * @param weight The number of * Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE"))-byte * units this VL should be allowed to transfer on each turn * */ public void addHighPriority(int VLNumber, int weight){ addEntry(VLNumber, weight, highPriority); } /** * Sets the low priority queue of this output port */ public void setLowPriotity(ListEntry[] queue){ this.lowPriority = queue; } /** * Adds an entry of low priority on this output port. * @param VLNumber The Virtual lane to add an entry for * @param weight The number of * Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE"))byte * units this VL should be allowed to transfer on each turn * */ public void addLowPriority(int VLNumber, int weight){ addEntry(VLNumber, weight, lowPriority); } /** * Gets the sink of this output port */ public Unit getSink(){ return link.getSink(); } public String report(){ return "IBAOutputPort " +ID +", sending on VL "+ ((current==null?"none": ""+current.VLNumber)+ ", "+buffer[0].getSize()+" in buffer[0]"+ ", "+bytesTransferred+" bytes transferred." ); } /** * Number of bytes successfully transferred from the current packet */ protected int bytesTransferred; /** The current VL entry to transfer from */ protected ListEntry current; /** * The number of * Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE"))-byte * units sent on the current VL in this turn. */ 195 protected int unitsSentOnCurrentVL; /** * The limit of high-priority packets that can be sent before the * low-priority packets get to send some. 4K times this number bytes * can be sent before the low-priority can send. * <ul> * <li>A value of 255 indicates that the limit is unbounded.</li> * <li>A value of 0 indicates that only a single packet * from hi-pri can be sent.</li> * </ul> */ protected int limitOfHighPriority; /** The number of bytes sent on high priority */ protected int bytesSentOnHighPriority; /** * The Flow Control Total Blocks Sent. * Indicates how many blocks that are sent * on each VL since link initialization. * */ protected int FCTBS[]; /** The list entries of the high priority packets. */ protected ListEntry[] highPriority; /** The list entries of the low priority packets. */ protected ListEntry[] lowPriority; /** * The currently active priority * (equals either high or low priority). */ protected ListEntry[] active; /** * The lrhs of all the buffers where we have read the * lrh in order to find out if there’s enough room to * send the packet out on the link. */ protected IBAPacket.LRH[] sendingLRHs; /** The number of the current high priority entry */ protected int currentHighPriority; /** The number of the current low priority entry */ protected int currentLowPriority; /** * Holds the current state of the IBAOutputPort, * in respect to the act() method, i.e. * the output from the output port, <b>not</b> if we can send <i>to</i> * the outout port. * Is either READY or SENDING. */ protected int sendingState; protected final int READY = 0; protected final int SENDING = 1; /** * Indicates whether we have sent the lrh of the * packet we are currently sending (in <b>act</b>) */ 196 protected boolean lrhIsSent; /** * For logging and debugging */ public int totalNumberOfPacketsSent; /** * Indicates when the transient period is over, * so we should start gathering data. */ protected int transientThreshold; /** * Indicates that the transient period is over */ protected boolean transientPeriod; /** * The state of all the input VLs */ protected VLstate[] VLstates; public class VLstate{ public int state; public byte[] lrh; public int numberOfBytesReceived; // For logging public int totalNumberOfPacketsSent = 0; // protected VLstate(){ init(); } protected void init(){ state = READY; lrh = new byte[IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE]; numberOfBytesReceived = 0; } protected void reset(){ init(); } public public public public static static static static final final final final int int int int READY CLEAR_TO_SEND RECEIVING_HEAD RECEIVING_PACKET = = = = 0; 1; 2; 3; } public class ListEntry{ /** The number of the virtual lane. */ public int VLNumber; /** * The number of * Integer.parseInt(System.getProperty("infiniband.BLOCK_SIZE"))-byte * units allowed to transfer from the VL. */ public int weight; } 197 } 198 no.uio.ifi.gruns.infiniband.IBARoutingTable package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; public class IBARoutingTable extends RoutingTable{ /** * Adds a possible output route for the supplied address. * Since the virtual lane is specified in the packet in * IBA, this information shouldn’t be part of the route. * @param a The address to add the route for * @param outgoingPortNumber The output port to send the packets on */ public void addRoute(Address a, int outgoingPortNumber){ super.addRoute(a, outgoingPortNumber, -1); } /** * Deletes a possible output route for the supplied address * Since the virtual lane is specified in the packet in * IBA, this information shouldn’t be part of the route. * @param a The address to delete a route for * @param outgoingPortNumber The port number of the route */ public void deleteRoute(Address a, int outgoingPortNumber){ super.deleteRoute(a, outgoingPortNumber, -1); } /** * Looks up the next hop for the supplied header */ public Route lookUp(IBAPacket.LRH lrh){ Route retVal = super.lookUp(new Address(lrh.DLID())); if (retVal != null){ retVal.VLNumber = lrh.VL(); } // If we just return retVal, for some reason // this method returns exactly the _same_ object each time, // and of course this is not what is desired. If we then // look up another packet, the previous packets will // get the same output route. Route retVal2 = new Route(); retVal2.portNumber = retVal.portNumber; retVal2.VLNumber = retVal.VLNumber; return retVal2; } public String report(){ return "no.uio.ifi.gruns.infiniband.IBARoutingTable"; } } 199 no.uio.ifi.gruns.infiniband.IBAUnitComparator package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; import no.uio.ifi.tools.*; import java.util.Comparator; public class IBAUnitComparator implements Comparator public int compare(Object o1, Object o2) { { int compVal=0; /* if (! (o1.getClass().getName().startsWith("IBA") && o2.getClass().getName().startsWith("IBA")) ) { throw new ClassCastException("Can only compare IBA units!!"); } */ String[] s = {(String) o1, (String) o2}; int int int int int addrPrefixPos[] = new int[2]; pointPos[] = new int[2]; addrSuffixEnd[] = new int[2]; addrPrefix[] = new int[2]; addrSuffix[] = new int[2]; for (int i = 0; i < 2; i++) { // Find position of first digit for (addrPrefixPos[i]=0; addrPrefixPos[i] < s[i].length(); addrPrefixPos[i]++) if (Character.isDigit(s[i].charAt(addrPrefixPos[i]))) break; { } // Find position of ".". for (pointPos[i]=addrPrefixPos[i]; pointPos[i] < if (s[i].charAt(pointPos[i]) == ’.’) break; s[i].length(); pointPos[i]++) { } // Find last digit after ".". for (addrSuffixEnd[i] = pointPos[i]+1; addrSuffixEnd[i] < s[i].length(); if (!Character.isDigit(s[i].charAt(addrSuffixEnd[i]))) break; addrSuffixEnd[i]++) { } /* System.err.println("s = \""+s[i]+"\""); System.err.println("prefixpos = "+addrPrefixPos[i]+", pointpos = "+pointPos[i]+ ", addrSuffixEnd = "+addrSuffixEnd[i]); */ addrPrefix[i] = Integer.parseInt(s[i].substring(addrPrefixPos[i], pointPos[i])); addrSuffix[i] = Integer.parseInt(s[i].substring(pointPos[i]+1, addrSuffixEnd[i])); //System.err.println("Prefix = "+addrPrefix[i]+", suffix = "+addrSuffix[i]); } if } (addrPrefix[0] < addrPrefix[1]) { compVal = -1; else if (addrPrefix[0] == addrPrefix[1]) if (addrSuffix[0] < addrSuffix[1]) { { compVal = -1; } else if } else (addrSuffix[0] == addrSuffix[1]) { compVal = 0; { 200 compVal = 1; } } else return compVal = 1; compVal; } public boolean equals(Object obj) return obj.equals(this); { } } 201 no.uio.ifi.gruns.infiniband.IBAConnectionTools package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; public class IBAConnectionTools{ // {{{ public static void setKernel(Kernel k) /** * Sets the kernel to use for the links. */ public static void setKernel(Kernel k){ kernel = k; } // }}} // {{{ public static void connect(IBASourceNode s, int port, IBASwitch sw, int inputPort) /** * Connect a source node <tt>s</tt> on port <tt>port</tt> * to a switch <tt>sw</tt> on inpurt port <tt>inputPort</tt>. */ public static void connect(IBASourceNode s, int port, IBASwitch sw, int inputPort){ // {{{ DEBUG /* Reporter.debugln("Connecting source node "+s.getID()+", port " +port+ " to switch "+sw.getID()+", input port "+inputPort); */ // }}} Link tmpLink = new Link(kernel); s.attach(tmpLink, port); sw.attachInput(tmpLink, inputPort); // Update the ID on the Link. -> "+sw.getID()+")"); tmpLink.setID("("+s.getID()+" } // }}} // {{{ public static void connect(IBASwitch sw1, int outputPort, IBASwitch sw2, int inputPort) /** * Connect a switch <tt>sw1</tt> on output port <tt>outputPort</tt> * to another switch <tt>sw2</tt> on input port <tt>inputPort</tt>. */ public static void connect(IBASwitch sw1, int outputPort, IBASwitch sw2, int inputPort){ // {{{ DEBUG /* Reporter.debugln("("+sw1.getID()+", "+outputPort+") -> ("+ sw2.getID()+", "+inputPort+")"); */ // }}} Link tmpLink = new Link(kernel); sw1.attachOutput(tmpLink, outputPort); sw2.attachInput(tmpLink, inputPort); // Update the ID on the Link. -> "+sw2.getID()+")"); tmpLink.setID("("+sw1.getID()+" } // }}} 202 // {{{ public static void connect(IBASwitch sw, int outputPort, IBADrainNode d, int port) /** * Connect a switch <tt>sw</tt> on output port <tt>outputPort</tt> * to a drain node <tt>d</tt> on port <tt>port</tt>. */ public static void connect(IBASwitch sw, int outputPort, IBADrainNode d, int port){ // {{{ DEBUG /* Reporter.debugln("Connecting switch "+sw.getID()+", outputPort "+outputPort+ " to drain node "+d.getID()+", port "+port); */ // }}} Link tmpLink = new Link(kernel); sw.attachOutput(tmpLink, outputPort); d.attach(tmpLink, port); // Update the ID on the Link. -> "+d.getID()+")"); tmpLink.setID("("+sw.getID()+" } // }}} // {{{ public static IBASwitch[] simplexGrid(int width, int height, int numberOfVLs) public static IBASwitch[] simplexGrid(int width, int height, int numberOfVLs){ int numInputs = 3; int numOutputs = 3; int numVLs = numberOfVLs; IBASwitch[] ibasw = new IBASwitch[width*height]; // Create all switches for (int i=0; i < ibasw.length; i++){ ibasw[i] = new IBASwitch(numInputs, numOutputs, numVLs, "Switch "+(i),kernel); } // Connect switches in grid int current; int rowOffset; ////// // Special case: height == 1 ////// if (height == 1){ // All but last column for (int i=0; i < width-1; i++){ current = i; // east connect(ibasw[current], 1, ibasw[current + 1], 1); } // Last column // no connections!" } else{ ////// // Top row ////// 203 if (height > 1){ // All but last column for (int i=0; i < width-1; i++){ current = i; // east connect(ibasw[current], 1, ibasw[current + 1], 1); // south connect(ibasw[current], 2, ibasw[current + width], 0); } // Last column current = width-1; connect(ibasw[current], 2, ibasw[current + width], 0); } ////// // Inner rows ////// if (height > 2){ for (int j=1; j < height-1; j++){ rowOffset = width*j; // All but last column for (int i=0; i < width-1; i++){ current = i + rowOffset; // north connect(ibasw[current], 0, ibasw[current - width], 2); connect(ibasw[current], 2, ibasw[current + width], 0); // east connect(ibasw[current], 1, ibasw[current + 1], 1); // south connect(ibasw[current], 2, ibasw[current + width], 0); } // Last column current = width-1 + rowOffset; // north connect(ibasw[current], 0, ibasw[current - width], 2); // south connect(ibasw[current], 2, ibasw[current + width], 0); } } ////// // Bottom row ////// if (height > 1){ rowOffset = width * (height-1); // All but last column for (int i=0; i < width-1; i++){ current = i + rowOffset; // north connect(ibasw[current], 0, ibasw[current - width], 2); // east connect(ibasw[current], 1, ibasw[current + 1], 1); } // Last column current = width-1 + rowOffset; 204 // north connect(ibasw[current], 0, ibasw[current - width], 2); } } return ibasw; } // }}} // {{{ public static IBASwitch[] simplexGrid(int width, int height, ..., bufferSize) public static IBASwitch[] simplexGrid(int width, int height, int numberOfVLs, int inputBufferSize, int outputBufferSize){ int numInputs = 3; int numOutputs = 3; int numVLs = numberOfVLs; IBASwitch[] ibasw = new IBASwitch[width*height]; // Create all switches for (int i=0; i < ibasw.length; i++){ ibasw[i] = new IBASwitch(numInputs, numOutputs, numVLs, inputBufferSize, outputBufferSize, "Switch "+(i),kernel); } // Connect switches in grid int current; int rowOffset; ////// // Special case: height == 1 ////// if (height == 1){ // All but last column for (int i=0; i < width-1; i++){ current = i; // east connect(ibasw[current], 1, ibasw[current + 1], 1); } // Last column // no connections!" } else{ ////// // Top row ////// if (height > 1){ // All but last column for (int i=0; i < width-1; i++){ current = i; // east connect(ibasw[current], 1, ibasw[current + 1], 1); // south connect(ibasw[current], 2, ibasw[current + width], 0); } // Last column 205 current = width-1; connect(ibasw[current], 2, ibasw[current + width], 0); } ////// // Inner rows ////// if (height > 2){ for (int j=1; j < height-1; j++){ rowOffset = width*j; // All but last column for (int i=0; i < width-1; i++){ current = i + rowOffset; // north connect(ibasw[current], 0, ibasw[current - width], 2); connect(ibasw[current], 2, ibasw[current + width], 0); // east connect(ibasw[current], 1, ibasw[current + 1], 1); // south connect(ibasw[current], 2, ibasw[current + width], 0); } // Last column current = width-1 + rowOffset; // north connect(ibasw[current], 0, ibasw[current - width], 2); // south connect(ibasw[current], 2, ibasw[current + width], 0); } } ////// // Bottom row ////// if (height > 1){ rowOffset = width * (height-1); // All but last column for (int i=0; i < width-1; i++){ current = i + rowOffset; // north connect(ibasw[current], 0, ibasw[current - width], 2); // east connect(ibasw[current], 1, ibasw[current + 1], 1); } // Last column current = width-1 + rowOffset; // north connect(ibasw[current], 0, ibasw[current - width], 2); } } return ibasw; } // }}} 206 // {{{ public static IBASwitch[] duplexGrid(int width, int height, int numberOfVLs) /** * Connects switches in a duplex grid, that is all links are bi-directional, * there’s always an input port for every output port and vice versa. * * Switches look like this, with the input and output ports numbered * as indicated below: * <pre> * * 1 1 * ^ | * | v * +------+ * 0 ->| |-> 2 * | | * 0 <-| |<- 2 * +------+ * | ^ * v | * 3 3 * </pre> */ public static IBASwitch[] duplexGrid(int width, int height, int numberOfVLs){ int numInputs = 4; int numOutputs = 4; int numVLs = numberOfVLs; IBASwitch[] ibasw = new IBASwitch[width*height]; // Create all switches for (int i=0; i < ibasw.length; i++){ ibasw[i] = new IBASwitch(numInputs, numOutputs, numVLs, "Switch "+(i),kernel); } // Connect switches in grid int current; int rowOffset; ////// // Special case: height == 1 ////// if (height == 1){ // First column // only east current = 0; connect(ibasw[current], 2, ibasw[current + 1], 0); // All but first and last column for (int i=1; i < width-1; i++){ current = i; // east connect(ibasw[current], 2, ibasw[current + 1], 0); // west connect(ibasw[current], 0, ibasw[current - 1], 2); } 207 // Last column // only west current = width-1; connect(ibasw[current], 0, ibasw[current - 1], 2); } else{ ////// // Top row ////// if (height > 1){ // First column // only east and south current = 0; // east connect(ibasw[current], 2, ibasw[current + 1], 0); // south connect(ibasw[current], 3, ibasw[current + width], 1); // All but first and last column for (int i=1; i < width-1; i++){ current = i; // east connect(ibasw[current], 2, ibasw[current + 1], 0); // west connect(ibasw[current], 0, ibasw[current - 1], 2); // south connect(ibasw[current], 3, ibasw[current + width], 1); } // Last column // only west and south current = width-1; // west connect(ibasw[current], 0, ibasw[current - 1], 2); // south connect(ibasw[current], 3, ibasw[current + width], 1); } ////// // Inner rows ////// if (height > 2){ for (int j=1; j < height-1; j++){ rowOffset = width*j; // First column current = 0 + rowOffset; // north connect(ibasw[current], 1, ibasw[current - width], 3); 208 // east connect(ibasw[current], 2, ibasw[current + 1], 0); // south connect(ibasw[current], 3, ibasw[current + width], 1); // All but first and last column for (int i=1; i < width-1; i++){ current = i + rowOffset; // west connect(ibasw[current], 0, ibasw[current - 1], 2); // north connect(ibasw[current], 1, ibasw[current - width], 3); // east connect(ibasw[current], 2, ibasw[current + 1], 0); // south connect(ibasw[current], 3, ibasw[current + width], 1); } // Last column current = width-1 + rowOffset; // west connect(ibasw[current], 0, ibasw[current - 1], 2); // north connect(ibasw[current], 1, ibasw[current - width], 3); // south connect(ibasw[current], 3, ibasw[current + width], 1); } } ////// // Bottom row ////// if (height > 1){ rowOffset = width * (height-1); // First column current = 0 + rowOffset; // north connect(ibasw[current], 1, ibasw[current - width], 3); // east connect(ibasw[current], 2, ibasw[current + 1], 0); // All but first and last column for (int i=1; i < width-1; i++){ current = i + rowOffset; // west connect(ibasw[current], 0, ibasw[current - 1], 2); // north connect(ibasw[current], 1, ibasw[current - width], 3); // east connect(ibasw[current], 2, ibasw[current + 1], 0); } 209 // Last column current = width-1 + rowOffset; // north connect(ibasw[current], 1, ibasw[current - width], 3); // west connect(ibasw[current], 0, ibasw[current - 1], 2); } } return ibasw; } // }}} // {{{ public static void buildDefaultRoutingTable(IBASwitch[] coreIbaSw, IBAStiwch[] edgeIbaSw) /** *<p> * Builds a default routing table in the net supplied. The edge switches should * be connected to the core switches, and the core switches should be connected * both ways, in a 2D mesh (grid). The routing that is set up, makes the switches * first forward data vertically, then horizontally. This is to avoid routing loops. *</p> *<p> * The core switches should be aligned from the top left corner of the mesh. *</p> *<p> * The egde switches should be aligned around the core switches, starting at top left. * In the corners, the edge routers should be connected to the top or bottom link of the core * switches, not the left or right. * The suffix of the addresses on the drain nodes should start at 0 on the top left, * then increase by one as we move around the mesh. *</p> *<p> *<b>A simple example with a 3x3 mesh:</b> * <tt>Ex</tt> are edge switches, and <tt>Cx</tt> are core switches. *<pre> * E0 E1 E2 * | | | * * C0 C1 C2 * | | | * E8 - C3 - C4 - C5 - E3 * | | | * C6 - C7 - C8 * * | | | * E6 E5 E4 * */ public static void buildDefaultRoutingTable(IBASwitch[] coreIbaSw, IBASwitch[] edgeIbaSw, int width, int height) { // Constants to ease port number addressing int WEST=0,NORTH=1,EAST=2,SOUTH=3; // Build the routing table, starting from upper left. // Core routers 210 // Find addresses of corners Address topLeft, topRight, bottomLeft, bottomRight; IBADrainNode dest=null; IBASwitch tmpSw=null; String addr=null; int drainNodePort=-1; int current=0; // Top/North row drainNodePort = 0; for (current = 0; current < width; current ++) { tmpSw = (IBASwitch) coreIbaSw[current].getOutNeighbour(NORTH); dest = (IBADrainNode) tmpSw.getOutNeighbour(drainNodePort); addr = dest.getOwnAddress().toString(); // Top row of core mesh // The ones to the left of current for (int i=0; i < current; i++) { coreIbaSw[i].addRoutingInterval(addr, EAST); } // Current coreIbaSw[current].addRoutingInterval(addr, NORTH); // The ones to the east of current for (int i=current + 1; i < width; i++) { coreIbaSw[i].addRoutingInterval(addr, WEST); } // Rest of mesh (just send up). for (int i = width; i < coreIbaSw.length; i++) { coreIbaSw[i].addRoutingInterval(addr, NORTH); } } // Right/East row for (int currentRow = 1 ; currentRow < height-1; currentRow++) { int offset = 0; current = (currentRow+1) * width - 1; tmpSw = (IBASwitch) coreIbaSw[current].getOutNeighbour(EAST); dest = (IBADrainNode) tmpSw.getOutNeighbour(drainNodePort); addr = dest.getOwnAddress().toString(); // The rows above current row for (int row = 0; row < currentRow; row++) { offset = row * width; for (int col=0; col < width; col++) { coreIbaSw[offset + col].addRoutingInterval(addr, SOUTH); } } // Current row offset = currentRow * width; for (int col=0; col < width; col++) { /* // DEBUG Reporter.println("Switch "+(offset+col)+", sending address "+addr+ " to output port "+EAST); */ 211 coreIbaSw[offset + col].addRoutingInterval(addr, EAST); } // The rows below current row for (int row = currentRow + 1; row < height; row++) { offset = row * width; for (int col=0; col < width; col++) { coreIbaSw[offset + col].addRoutingInterval(addr, NORTH); } } } // Bottom/South row for (current = width*(height-1); current < coreIbaSw.length; current ++) { tmpSw = (IBASwitch) coreIbaSw[current].getOutNeighbour(SOUTH); dest = (IBADrainNode) tmpSw.getOutNeighbour(drainNodePort); addr = dest.getOwnAddress().toString(); // Bottom row of core mesh // The ones to the left of current for (int i=width*(height-1); i < current; i++) { coreIbaSw[i].addRoutingInterval(addr, EAST); } // Current coreIbaSw[current].addRoutingInterval(addr, SOUTH); // The ones to the east of current for (int i=current + 1; i < coreIbaSw.length; i++) { coreIbaSw[i].addRoutingInterval(addr, WEST); } // Rest of mesh (just send down). for (int i = 0; i < width*(height-1); i++) { coreIbaSw[i].addRoutingInterval(addr, SOUTH); } } // Left/West row for (int currentRow = 1 ; currentRow < height-1; currentRow++) { int offset = 0; current = (currentRow) * width; tmpSw = (IBASwitch) coreIbaSw[current].getOutNeighbour(WEST); dest = (IBADrainNode) tmpSw.getOutNeighbour(drainNodePort); addr = dest.getOwnAddress().toString(); // The rows above current row for (int row = 0; row < currentRow; row++) { offset = row * width; for (int col=0; col < width; col++) { coreIbaSw[offset + col].addRoutingInterval(addr, SOUTH); } } // Current row offset = currentRow * width; for (int col=0; col < width; col++) { coreIbaSw[offset + col].addRoutingInterval(addr, WEST); } // The rows below current row 212 for (int row = currentRow + 1; row < height; row++) { offset = row * width; for (int col=0; col < width; col++) { coreIbaSw[offset + col].addRoutingInterval(addr, NORTH); } } } // Edge switches for (int i = 0; i < edgeIbaSw.length; i++) { addr = ((IBADrainNode)edgeIbaSw[i].getOutNeighbour(drainNodePort)). getOwnAddress().toString(); // Add all out /* edgeIbaSw[i].addRoutingInterval("1.0-1.255", 1); edgeIbaSw[i].deleteRoutingInterval(addr, 1); edgeIbaSw[i].addRoutingInterval(addr, drainNodePort); */ // All up to current int suff = Integer.parseInt(addr.substring(addr.indexOf(".")+1)); int prev = suff-1; String prevAddr = null; if (prev >= 0) { prevAddr = "1."+(prev); //Reporter.println("prevAddr = "+prevAddr+", addr = "+addr); edgeIbaSw[i].addRoutingInterval("1.0-"+prevAddr, 1); } // Add current edgeIbaSw[i].addRoutingInterval(addr, drainNodePort); // All above current int next = suff +1; String nextAddr = null; if (next < 255) { nextAddr = "1."+(next); //Reporter.println("nextAddr = "+nextAddr+", addr = "+addr); edgeIbaSw[i].addRoutingInterval(nextAddr+"-1.255", 1); } } } // }}} // {{{ public static void buildOptimisticRoutingTable(IBASwitch[] coreIbaSw, IBAStiwch[] edgeIbaS /** *<p> * Builds an optimistic routing table in the net supplied. The edge switches should * be connected to the core switches, and the core switches should be connected * both ways, in a 2D mesh (grid). The routing that is set up, makes the switches * forward packets most optimal, at the risk of routing loops, but it should improve the * total capacity of the net drastically. *</p> *<p> * The core switches should be aligned from the top left corner of the mesh. *</p> *<p> * The egde switches should be aligned around the core switches, starting at top left. * In the corners, the edge routers should be connected to the top or bottom link of the core * switches, not the left or right. * The suffix of the addresses on the drain nodes should start at 0 on the top left, 213 * then increase by one as we move around the mesh. *</p> *<p> *<b>A simple example with a 3x3 mesh:</b> * <tt>Ex</tt> are edge switches, and <tt>Cx</tt> are core switches. *<pre> * E0 E1 E2 * | | | * * C0 C1 C2 * | | | * E8 - C3 - C4 - C5 - E3 * | | | * C6 - C7 - C8 * * | | | * E6 E5 E4 * */ public static void buildOptimisticRoutingTable(IBASwitch[] coreIbaSw, IBASwitch[] edgeIbaSw, int width, int height) { // Constants to ease port number addressing int WEST=0,NORTH=1,EAST=2,SOUTH=3; // Build the routing table, starting from upper left. // Core routers // Find addresses of corners Address topLeft, topRight, bottomLeft, bottomRight; IBADrainNode dest=null; IBASwitch tmpSw=null; String addr=null; int drainNodePort=-1; int currCol=0; int offset = 0; int current = 0; // Top/North row drainNodePort = 0; for (currCol = 0; currCol < width; currCol ++) { tmpSw = (IBASwitch) coreIbaSw[currCol].getOutNeighbour(NORTH); dest = (IBADrainNode) tmpSw.getOutNeighbour(drainNodePort); addr = dest.getOwnAddress().toString(); // All rows for (int row = 0; row < height; row++) { offset = row * width; current = offset + currCol; /* // DEBUG Reporter.debugln("Row "+row); Reporter.debugln("=================================================="); // */ // The ones to the left of currCol for (int i=offset; i < current; i++) { /* 214 // DEBUG Reporter.debugln("Switch "+i+": sending packets to "+addr+" EAST"); // */ coreIbaSw[i].addRoutingInterval(addr, EAST); } // Current /* // DEBUG Reporter.debugln("Switch "+current+": sending packets to "+addr+" NORTH"); // */ coreIbaSw[current].addRoutingInterval(addr, NORTH); // The ones to the east of current for (int i=current + 1; i < offset + width; i++) { /* // DEBUG Reporter.debugln("Switch "+i+": sending packets to "+addr+" WEST"); // */ coreIbaSw[i].addRoutingInterval(addr, WEST); } } } // Right/East row for (int currentRow = 1 ; currentRow < height-1; currentRow++) { offset = 0; current = (currentRow+1) * width - 1; tmpSw = (IBASwitch) coreIbaSw[current].getOutNeighbour(EAST); dest = (IBADrainNode) tmpSw.getOutNeighbour(drainNodePort); addr = dest.getOwnAddress().toString(); // The rows above current row for (int row = 0; row < currentRow; row++) { offset = row * width; for (int col=0; col < width; col++) { /* // DEBUG Reporter.debugln("Switch "+(offset+col)+": sending packets to "+addr+" SOUTH"); // */ coreIbaSw[offset + col].addRoutingInterval(addr, SOUTH); } } // Current row offset = currentRow * width; for (int col=0; col < width; col++) { /* // DEBUG Reporter.debugln("Switch "+(offset+col)+": sending packets to "+addr+" EAST"); // */ coreIbaSw[offset + col].addRoutingInterval(addr, EAST); } // The rows below current row for (int row = currentRow + 1; row < height; row++) { offset = row * width; 215 for (int col=0; col < width; col++) { /* // DEBUG Reporter.debugln("Switch "+(offset+col)+": sending packets to "+addr+" NORTH"); // */ coreIbaSw[offset + col].addRoutingInterval(addr, NORTH); } } } // Bottom/South row for (currCol = 0; currCol < width; currCol ++) { tmpSw = (IBASwitch) coreIbaSw[(height-1) * width + currCol].getOutNeighbour(SOUTH); dest = (IBADrainNode) tmpSw.getOutNeighbour(drainNodePort); addr = dest.getOwnAddress().toString(); // All rows for (int row = 0; row < height; row++) { offset = row * width; current = offset + currCol; /* // DEBUG Reporter.debugln("Row "+row); Reporter.debugln("=================================================="); // */ // The ones to the left of current for (int i=offset; i < current; i++) { /* // DEBUG Reporter.debugln("Switch "+i+": sending packets to "+addr+" EAST"); // */ coreIbaSw[i].addRoutingInterval(addr, EAST); } // Current /* // DEBUG Reporter.debugln("Switch "+current+": sending packets to "+addr+" SOUTH"); // */ coreIbaSw[current].addRoutingInterval(addr, SOUTH); // The ones to the east of current for (int i=current + 1; i < offset + width; i++) { /* // DEBUG Reporter.debugln("Switch "+i+": sending packets to "+addr+" WEST"); // */ coreIbaSw[i].addRoutingInterval(addr, WEST); } } } // Left/West row for (int currentRow = 1 ; currentRow < height-1; 216 currentRow++) { offset = 0; current = (currentRow) * width; tmpSw = (IBASwitch) coreIbaSw[current].getOutNeighbour(WEST); dest = (IBADrainNode) tmpSw.getOutNeighbour(drainNodePort); addr = dest.getOwnAddress().toString(); // The rows above current row for (int row = 0; row < currentRow; row++) { offset = row * width; for (int col=0; col < width; col++) { /* // DEBUG Reporter.debugln("Switch "+(offset+col)+": sending packets to "+addr+" SOUTH"); // */ coreIbaSw[offset + col].addRoutingInterval(addr, SOUTH); } } // Current row offset = currentRow * width; for (int col=0; col < width; col++) { /* // DEBUG Reporter.debugln("Switch "+(offset+col)+": sending packets to "+addr+" WEST"); // */ coreIbaSw[offset + col].addRoutingInterval(addr, WEST); } // The rows below current row for (int row = currentRow + 1; row < height; row++) { offset = row * width; for (int col=0; col < width; col++) { /* // DEBUG Reporter.debugln("Switch "+(offset+col)+": sending packets to "+addr+" NORTH"); // */ coreIbaSw[offset + col].addRoutingInterval(addr, NORTH); } } } // Edge switches for (int i = 0; i < edgeIbaSw.length; i++) { addr = ((IBADrainNode)edgeIbaSw[i].getOutNeighbour(drainNodePort)). getOwnAddress().toString(); // Add all out /* edgeIbaSw[i].addRoutingInterval("1.0-1.255", 1); edgeIbaSw[i].deleteRoutingInterval(addr, 1); edgeIbaSw[i].addRoutingInterval(addr, drainNodePort); */ // All up to current int suff = Integer.parseInt(addr.substring(addr.indexOf(".")+1)); int prev = suff-1; String prevAddr = null; if (prev >= 0) { prevAddr = "1."+(prev); 217 //Reporter.println("prevAddr = "+prevAddr+", addr = "+addr); edgeIbaSw[i].addRoutingInterval("1.0-"+prevAddr, 1); } // Add current edgeIbaSw[i].addRoutingInterval(addr, drainNodePort); // All above current int next = suff +1; String nextAddr = null; if (next < 255) { nextAddr = "1."+(next); //Reporter.println("nextAddr = "+nextAddr+", addr = "+addr); edgeIbaSw[i].addRoutingInterval(nextAddr+"-1.255", 1); } } } // }}} /** Used when connecting two units.*/ //private static Link tmpLink; private static Kernel kernel; } 218 no.uio.ifi.gruns.infiniband.IBADelayedOutputPort package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; /** * The output ports on IBA units, delayed version. * Waits to see if there is coming data on the high priority * VL if it has just finished sending a packet on high priority. * The intention is to avoid sending a packet from the low priority queue * when there is a packet on its way on high priority, but it’s just delayed * from the processing in the swithch. * * However, this doesn’t work very well, because it is impossible to know anything * about the next packet on a particular VL when the switch has not yet read this packet * since it hasn’t arrived in the switch yet. */ public class IBADelayedOutputPort extends IBAOutputPort{ /** * @param numberOfVirtualLanes The number of virtual lanes * @param bufferSize The size of the output buffers * @param kernel The kernel to use for simulation */ public IBADelayedOutputPort(int numberOfVirtualLanes, int bufferSize, Kernel kernel, IBADelayedSwitch source, int portNumber){ super(numberOfVirtualLanes, bufferSize, kernel); this.source = source; this.portNumber = portNumber; } protected void updateCurrent(){ ListEntry le; // Reset sendingStates lrhIsSent = false; bytesTransferred = 0; // Check if we have exceeded the limit of high priority // packets. If we have, check the low priority queue first. if ( // limit == 255 means no limit limitOfHighPriority < 255 && ( // limit == 0 means we could send one packet. (limitOfHighPriority == 0 && bytesSentOnHighPriority > (limitOfHighPriority * 4096)) || (limitOfHighPriority > 0 && bytesSentOnHighPriority >= (limitOfHighPriority * 4096)) ) ) { bytesSentOnHighPriority = 0; le = findNextListEntry(lowPriority, currentLowPriority); if (le != null){ // Packets to send on Low Priority active = lowPriority; 219 sendingState = SENDING; }else{ le = findNextListEntry(highPriority, currentHighPriority); if (le != null){// Packets to send on High Priority active = highPriority; sendingState = SENDING; } } } // If not, check the high priority first (usual case) else{ // Find next to send from le = findNextListEntry(highPriority, currentHighPriority); if (le != null){// Packets to send on High Priority // {{{ DEBUG Reporter.debugln(ID+": Sending from high priority."); // }}} active = highPriority; sendingState = SENDING; } else { // No packets to send on High Priority // // // // // // // // // // // // Check to see if there’s data on its way on high pri, and wait as many time steps as the processing time in the switch so that we avoid sending from low priority and lose a lot of time for the high pri VLs. If we have data coming in on a high-pri VL, just set current to null, and then the we will find out below that we have data to send (on low priority), but not right now (since current is null), and we will reshedule ourselves. Then, eventually, when the data is ready from the switch, we will have the data available in our buffers, so we don’t need to check how many time steps the switch has left of the packet processing time. // // // // // // // // // TODO: The problem is that if we are only receiving packets on low priority, and the switch is sending out packets on high priority to some other output port, we could end up waiting forever, since no high priority packets will ever come our way. Another problem is that we don’t have any way of finding out if the packets are high or low priority, and which VL the packets are sent on before the switch has finished processing them. IBASwitch.VLstate[][] states = source.getStates(); // Loop through all high priority VLs and find out if // we have data in the coming on any of them. ListEntry entry; boolean dataIsOnItsWay = false; for (int i=0; i < this.highPriority.length; i++){ entry = highPriority[i]; // {{{ Kommentert ut /* // {{{ DEBUG Reporter.debug("Checking high priority entry "+i); if (entry == null){ // {{{ Reporter.println("...empty."); }else{ 220 Reporter.println("."); } // }}} */ // }}} if (entry != null){ // Check if any of the states of the switch has the same VL // number as any of our high priority VLs. If it has, there’s a slight // chance that the packet on its way in the switch is actually coming here. for (int j=0; j < states.length; j++) { for (int k=0; k < states[j].length; k++) { // {{{ Kommentert ut /* // {{{ DEBUG Reporter.debugln("Checking input port "+j+", VL "+k+"."); Reporter.debugln("states["+j+"]["+k+"] = "+states[j][k]); Reporter.debugln("states["+j+"]["+k+"].outputPort = "+ states[j][k].outputPort); Reporter.debug("states["+j+"]["+k+"].state = "); switch(states[j][k].state){ case IBASwitch.VLstate.READY: Reporter.println("READY"); break; case IBASwitch.VLstate.RECEIVING_HEADER: Reporter.println("RECEIVING_HEADER"); break; case IBASwitch.VLstate.PROCESSING_HEADER: Reporter.println("PROCESSING_HEADER"); break; case IBASwitch.VLstate.VIRTUAL_CUT_THROUGH: Reporter.println("VIRTUAL_CUT_THROUGH"); break; case IBASwitch.VLstate.WAITING_FOR_OUTPUT_LINK: Reporter.println("WAITING_FOR_OUTPUT_LINK"); break; case IBASwitch.VLstate.FORWARD: Reporter.println("FORWARD"); break; } // }}} */ // }}} if (states[j][k] != null && states[j][k].state == IBADelayedSwitch.VLstate.PROCESSING_HEADER ){ // {{{ Kommentert ut /* // {{{ DEBUG Reporter.debugln("Found incoming data on VL number "+ entry.VLNumber+"."); // }}} */ // }}} dataIsOnItsWay = true; break; } } } 221 } } if (dataIsOnItsWay){ // {{{ DEBUG Reporter.debugln(ID+", pausing to wait for data on hi pri."); // }}} le = null; } else{// No data on its way // {{{ DEBUG Reporter.debugln(ID+": Sending from lowpriority."); // }}} bytesSentOnHighPriority = 0; le = findNextListEntry(lowPriority, currentLowPriority); if (le != null){ // Packets to send on Low Priority active = lowPriority; bytesSentOnHighPriority = 0; sendingState = SENDING; } } } } // Update the current list entry. this.current = le; // Finished if nothing to send if (current == null){ // Reschedule if we still have data to send // but are not able to send right now for (int i=0; i < numberOfVLs; i++){ if (!buffer[i].isEmpty()){ kernel.schedule(this, kernel.currentTime() + 1); break; } } }else{ // Send one block of data sending(); } // {{{ Kommentert ut /* // DEBUG if (ID.startsWith("Switch 0") //|| ID.startsWith("Switch 6") //|| ID.startsWith("Switch 7") ) { System.out.println("Time: "+kernel.currentTime()+", " +ID+": Selected VL "+current.VLNumber+". "+ "VL 1’s buffer is "+ (buffer[1].isEmpty()?"":"NOT ")+ "empty." ); } // */ // }}} } /** * The output port this output port is connected to. 222 * Used to find out how long to delay in updateCurrent. */ protected IBADelayedSwitch source; /** * The number this output port has on the switch is * connected to. */ protected int portNumber; } 223 no.uio.ifi.gruns.infiniband.IBADelayedOutputPort2 package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; /** * The output ports on IBA units, delayed version 2. * * Waits exactly the time a switch uses to process a packet before sending * the next packet, hoping that the next packet on high priority * whould have arrived by then. * * The intention is to avoid sending a packet from the low priority queue * when there is a packet on its way on high priority, and it is just delayed * from the processing in the swithch. */ public class IBADelayedOutputPort2 extends IBAOutputPort{ // {{{ Constructor /** * @param numberOfVirtualLanes The number of virtual lanes * @param bufferSize The size of the output buffers * @param kernel The kernel to use for simulation */ public IBADelayedOutputPort2(int numberOfVirtualLanes, int bufferSize, Kernel kernel, IBADelayedSwitch2 source, int portNumber){ super(numberOfVirtualLanes, bufferSize, kernel); this.source = source; this.portNumber = portNumber; } // }}} /** * Introduces a delay between packets. See class definition * for more information. */ protected void updateCurrent(){ ListEntry le; // Reset sendingStates lrhIsSent = false; bytesTransferred = 0; // Check if we have exceeded the limit of high priority // packets. If we have, check the low priority queue first. if ( // limit == 255 means no limit limitOfHighPriority < 255 && ( // limit == 0 means we could send one packet. (limitOfHighPriority == 0 && bytesSentOnHighPriority > (limitOfHighPriority * 4096)) || (limitOfHighPriority > 0 && bytesSentOnHighPriority >= (limitOfHighPriority * 4096)) ) 224 ) { bytesSentOnHighPriority = 0; le = findNextListEntry(lowPriority, currentLowPriority); if (le != null){ // Packets to send on Low Priority active = lowPriority; sendingState = SENDING; }else{ le = findNextListEntry(highPriority, currentHighPriority); if (le != null){// Packets to send on High Priority active = highPriority; sendingState = SENDING; } } } // If not, check the high priority first (usual case) else{ // Find next to send from le = findNextListEntry(highPriority, currentHighPriority); if (le != null){// Packets to send on High Priority // {{{ DEBUG Reporter.debugln(ID+": Sending from high priority."); // }}} active = highPriority; sendingState = SENDING; } else { // No packets to send on High Priority if (!haveWaitedForHiPri) { // INVALID_VALUE indicates that we have not // waited any time steps yet if (remainingDelay == INVALID_VALUE) { remainingDelay = Integer. parseInt(System.getProperty("infiniband.PACKET_PROCESSING_TIME")); // {{{ DEBUG Reporter.debugln(ID+": Starting to wait for hi pri. "+ remainingDelay+" time units left."); // }}} } // Decrease waiting timer remainingDelay--; // {{{ DEBUG Reporter.debugln(ID+": Waiting for high pri. "+remainingDelay+ " time units left."); // }}} if (remainingDelay == 0){ haveWaitedForHiPri = true; } } else{ // {{{ DEBUG Reporter.debugln(ID+": Sending from lowpriority."); // }}} 225 bytesSentOnHighPriority = 0; le = findNextListEntry(lowPriority, currentLowPriority); if (le != null){ // Packets to send on Low Priority active = lowPriority; bytesSentOnHighPriority = 0; sendingState = SENDING; } } } } // Update the current list entry. this.current = le; // Finished if nothing to send if (current == null){ // Reschedule if we still have data to send // but are not able to send right now for (int i=0; i < numberOfVLs; i++){ if (!buffer[i].isEmpty()){ kernel.schedule(this, kernel.currentTime() + 1); break; } } }else{ // Send one block of data sending(); } } /** * Sends a block from the current VL and checks if we are finished sending * the current packet. Reschedules if we are noe finished. */ protected void sending(){ // TODO: Small problem: When we are scheduled, and // have already read all the bytes in the packet, we should // start on the next packet, as of now, we only reset the state, // and don’t transfer any bytes. // // Shouldn’t happen, really, because when we have finished reading // all bytes, the state is reset, and we proceed to the next packet. Maybe // it’s not happening. Find out! sendBlockFromCurrent(); // Reschedule if not finished with current packet if (bytesTransferred < IBAPacketParser.packetLength(sendingLRHs[current. VLNumber])){ kernel.schedule(this, kernel.currentTime() + 1); } // Clear the lrh from this VL if we are finished // sending from it. Reset other parameters else if (bytesTransferred == IBAPacketParser.packetLength(sendingLRHs[current. VLNumber])){ sendingLRHs[current.VLNumber] = null; sendingState = READY; // for hi pri waiting 226 haveWaitedForHiPri = false; remainingDelay = INVALID_VALUE; // Logging VLstates[current.VLNumber].totalNumberOfPacketsSent++; // // For SmallNetwork.gridNetwork and PriTest, we need the if test // to avoid too much output. if (ID.startsWith("Switch 0") // || ID.startsWith("Switch 6") // || ID.startsWith("Switch 7") ){ /* // DEBUG Reporter.print("Time "+kernel.currentTime()+", "); Reporter.print(ID+": "); for (int i=0; i < numberOfVLs; i++){ Reporter.print("VL "+i+": "+VLstates[i].totalNumberOfPacketsSent+", "); } */ // LEAVE THIS... DON’T REMOVE! int totLo=0, totHi=0; double ratio; for (int i=0; i < lowPriority.length; i++){ if (lowPriority[i] != null){ totLo += VLstates[lowPriority[i].VLNumber].totalNumberOfPacketsSent; } } for (int i=0; i < highPriority.length; i++){ if (highPriority[i] != null){ totHi += VLstates[highPriority[i].VLNumber].totalNumberOfPacketsSent; } } IBAStatistics.reportPriorities(this, totHi, totLo); ratio = IBAStatistics.getPriorities(this); // /* Reporter.print("Ratio Hi/Lo = "+ (ratio==-1?"INF":Utils.formatDouble(ratio))); Reporter.println(); */ } // Reschedule if we still have data to send // but are not able to send right now for (int i=0; i < numberOfVLs; i++){ if (!buffer[i].isEmpty()){ kernel.schedule(this, kernel.currentTime() + 1); break; } } // For logging and debugging totalNumberOfPacketsSent++; } // Sanity check 227 else if (bytesTransferred > IBAPacketParser.packetLength(sendingLRHs[current. VLNumber])){ Reporter.error("IBAOutputPort.act: Too many bytes sent! "+ bytesTransferred +" > " + IBAPacketParser. packetLength(sendingLRHs[current. VLNumber])+"."); } // Insanity check ;) else{ throw new java.lang.RuntimeException("Serious error! "+ "Should be "+ "impossible!"); } } /** * The output port this output port is connected to. * Used to find out how long to delay in updateCurrent. */ protected IBADelayedSwitch2 source; /** * The number this output port has on the switch is * connected to. */ protected int portNumber; /** * The time left to wait for a packet on high priority * before sending one on low priority. */ protected int remainingDelay; /** * Indicates whether we have waited the time it takes for the * switch to process a packet to see if there is any data coming in * on high priority. */ protected boolean haveWaitedForHiPri; /** * To indicate that the remaining delay time counter is invalid. */ protected static int INVALID_VALUE = -42; } 228 no.uio.ifi.gruns.infiniband.IBADrainNode package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; import no.uio.ifi.tools.*; import java.util.Vector; public class IBADrainNode extends DrainNode implements Reporting{ public IBADrainNode(Kernel kernel){ super(kernel); } public IBADrainNode(int numberOfInputPorts, int numberOfVirtualLanes, Kernel kernel){ super(kernel); init(numberOfInputPorts, numberOfVirtualLanes, kernel); } public IBADrainNode(int numberOfInputPorts, int numberOfVirtualLanes, String ID, Kernel kernel){ this(numberOfInputPorts, numberOfVirtualLanes, kernel); this.ID = ID; for (int i=0; i < numberOfInputPorts; i++){ inputPorts[i].setID(ID+", input port "+i); } } private void init(int numberOfInputPorts, int numberOfVirtualLanes, Kernel kernel){ this.numberOfVLs = numberOfVirtualLanes; inputPorts = new IBAInputPort[numberOfInputPorts]; for (int i=0; i < numberOfInputPorts; i++){ inputPorts[i] = new IBAInputPort(numberOfVirtualLanes, kernel); inputPorts[i].setSink(this); } // Create input VL information holders states = new State[numberOfInputPorts][numberOfVirtualLanes]; for (int i=0; i < numberOfInputPorts; i++){ for (int j=0; j < states[i].length; j++){ states[i][j] = new State(); } } } /** * Sets the address of this drain node * @param The address as a string. */ public void setOwnAddress(String address){ this.address = new Address(address); } /** * Sets the address of this drain node * @param The address as an Address object. 229 */ public void setOwnAddress(Address address){ this.address = address; } /** * Gets the address of this drain node * @return The address as an Address object. */ public Address getOwnAddress(){ return this.address; } /** *<p> * Tells the drain node that there are more bytes ready * to be read from the input port. *</p> *<p> * Sometimes the drain node * could be scheduled at time N-2 to read from VL A at time N, * then be scheduled at time N-1 to read from VL B at time N+1. * When the drain node is run (at time N), it will read from both * VL A and VL B, since it has no way of telling who scheduled it. *<p> *<p> * When the drain node is then run at time N+1, it will try to * read from VL B, but there are no bytes waiting to be read there, * because they have already been read. We avoid this by keeping track * of how many bytes that wait to be read. (By using this method) */ public void increaseBytesReadyToBeReceived(IBAInputPort port, int VLNumber, int numBytes){ int portNum = -1; // find Port number for (int i=0; i < inputPorts.length; i++){ if (port == inputPorts[i]){ portNum = i; break; } } if (portNum == -1){ Reporter.error(ID+": Port "+port.getID()+" is not attached to me."); } states[portNum][VLNumber].numBytesReadyToReceive += numBytes; } /** * Stores a byte array in the right buffer. */ private void readBytes(int inputPortNumber, int VLNumber, byte[] fromBuffer){ for (int i=0; i < fromBuffer.length; i++){ // Check for consitency if (!inputPorts[inputPortNumber].hasDataOnVL(VLNumber)){ Reporter.error("IBADrainNode.readBytes(int, int, byte[]): "+ "No data to read! Read "+i+", tried "+ fromBuffer.length); } states[inputPortNumber][VLNumber].buffer.put(fromBuffer[i]); 230 } } /** * Receives bytes from the supplied input ports * on the supplied VL. */ private void readBytes(int inputPortNumber, int VLNumber, int numBytes){ int VL = states[inputPortNumber][VLNumber].lrh.VL(); /* // DEBUG Reporter.debugln("IBAPacketParser.packetLength(lrhs["+inputPortNumber+"]["+ VLNumber+"]) = "+ IBAPacketParser.packetLength(states[inputPortNumber][VLNumber].lrh)); Reporter.debugln("numBytesReceived["+inputPortNumber+"]["+ VLNumber+"]) = "+ states[inputPortNumber][VLNumber].numBytesReceived); Reporter.debugln("(IBAPacketParser.packetLength(lrhs["+inputPortNumber+"]["+ VLNumber+"]) = "+ (IBAPacketParser.packetLength(states[inputPortNumber][VLNumber].lrh))); Reporter.debugln("numBytes to receive = "+numBytes); // */ for (int i=0; i < numBytes; i++){ // Check for consistency if (! inputPorts[inputPortNumber].hasDataOnVL(VLNumber)){ Reporter.error("IBADrainNode.readBytes(int, int, int): "+ "No data on port "+inputPortNumber+", VL "+VLNumber+". "+ "Read "+i+", tried "+numBytes); } states[inputPortNumber][VL].buffer. put(inputPorts[inputPortNumber].receiveByte(VLNumber)); } // Update state variables states[inputPortNumber][VLNumber].numBytesReceived+=numBytes; states[inputPortNumber][VLNumber].numBytesReadyToReceive-=numBytes; } public void act(){ int numBytes=0; // Loop through all incoming VLs and read some data. for (int i=0; i < inputPorts.length; i++){ for (int j=0; j < inputPorts[i].numberOfVLs(); j++){ numBytes = 0; switch(states[i][j].state){ case State.READY: states[i][j].reset(); if (!inputPorts[i].hasDataOnVL(j) || states[i][j].numBytesReadyToReceive == 0){ break; }else{ states[i][j].state = State.RECEIVING_HEAD; } // fallthrough 231 case State.RECEIVING_HEAD: // Receive head byte [] tmpByte = new byte[IBAPacket.LRH_SIZE / IBAPacket.BITS_PER_BYTE]; for (int k=0; k < tmpByte.length; k++) { tmpByte[k] = inputPorts[i].receiveByte(j); states[i][j].numBytesReceived++; states[i][j].numBytesReadyToReceive--; } states[i][j].lrh = dummyPacket.new LRH(tmpByte); // assertion if (states[i][j].lrh.VL() != j){ Reporter.error("IBADrainNode.act: wrong VL number! "+ states[i][j].lrh.VL()+" != "+j); } // put the LRH in the right buffer readBytes(i, j, tmpByte); // Read more data from the link // (up to Integer.parseInt(System.getProperty( // "infiniband.BLOCK_SIZE")) bytes) /* numBytes = Math.min(Integer.parseInt( System.getProperty("infiniband.BLOCK_SIZE")) IBAPacket.LRH_SIZE/IBAPacket.BITS_PER_BYTE, IBAPacketParser.packetLength(states[i][j].lrh) states[i][j].numBytesReceived); */ numBytes = Math.min(Integer.parseInt( System.getProperty("infiniband.BLOCK_SIZE")) IBAPacket.LRH_SIZE/IBAPacket.BITS_PER_BYTE, Math.min(IBAPacketParser.packetLength(states[i][j].lrh) states[i][j].numBytesReceived, states[i][j].numBytesReadyToReceive)); readBytes(i, j, numBytes); /* // DEBUG Reporter.debugln(ID+", on port "+i+ ", VL "+j+": Received "+tmpByte.length+" + "+numBytes+" bytes"+ ", packet: "+states[i][j].numBytesReceived+" bytes."); // */ states[i][j].state = State.RECEIVING_CONTENTS; break; case State.RECEIVING_CONTENTS: // Read data from the link //(up to Integer.parseInt(System.getProperty( // "infiniband.BLOCK_SIZE")) bytes) numBytes = Math.min(Integer.parseInt( System.getProperty("infiniband.BLOCK_SIZE")), Math.min(IBAPacketParser.packetLength(states[i][j].lrh) states[i][j].numBytesReceived, states[i][j].numBytesReadyToReceive)); /* // DEBUG Reporter.debugln(ID+", port "+i+ 232 ", VL "+j+": Trying to receive "+numBytes+" bytes"); // */ readBytes(i, j, numBytes); /* // DEBUG Reporter.debugln(ID+", on port "+i+ ", VL "+j+": Received "+numBytes+" bytes"+ ", packet: "+states[i][j].numBytesReceived+" bytes."); // */ // Check if we are finished reading the packet if (states[i][j].numBytesReceived == IBAPacketParser.packetLength(states[i][j].lrh)){ tmpPacket = new IBAPacket(states[i][j].buffer.toByteArray()); /* // DEBUG Reporter.debugln("========>Received packet from "+ new Address(tmpPacket.lrh.SLID())); // */ // Store some statisics IBAStatistics.reportReceivedPacket(this, i, j, states[i][j].lrh, states[i][j].numBytesReceived); // Reset status states[i][j].state = State.READY; } // Internal check. If the test is true, there is // an internal error in the simulator program. else if (states[i][j].numBytesReceived > IBAPacketParser.packetLength(states[i][j].lrh)){ Reporter.error(ID+" IBADrainNode.act: received "+ states[i][j].numBytesReceived+ " on port "+i+", VL "+j); } // break; default: Reporter.error("IBADrainNode.act: Illegal state for port "+ i+", VL "+j+": "+states[i][j].state); } } } } /** * Attaches a link to this drain node on the * supplied input port number */ public void attach(Link l, int inputPortNumber) throws IllegalArgumentException { if (inputPorts == null){ throw new IllegalArgumentException("This drain node has "+ "no input ports."); } 233 if (inputPortNumber < 0 || inputPortNumber > inputPorts.length){ throw new IllegalArgumentException("Input port number out "+ "of range. Must be"+ "between 0 and "+ inputPorts.length); } inputPorts[inputPortNumber].attach(l); // Update the ID to be able to distinguish input ports from each other inputPorts[inputPortNumber].setID(ID+", port "+inputPortNumber); } public String report(){ int grandTotalNumberOfPackets = 0; int grandTotalNumberOfBytes = 0; IBAStatistics.IBADrainNodeStats stats; stats = IBAStatistics.getReceivedPacketStats(this); StringBuffer retVal = new StringBuffer(); retVal.append("IBADrainNode "+ID+": Total number of packets received = "); if (stats != null){ retVal.append(Utils.formatInt(stats.getTotalNumberOfReceivedPackets())+".\n"); java.util.HashMap map = stats.getPacketsReceivedPerSource(); java.util.Set keys = map.keySet(); java.util.Iterator it = keys.iterator(); while(it.hasNext()){ String key = (String) it.next(); Received from source "+ retVal.append(" key+": "+ Utils.formatInt(((Integer) map.get(key)).intValue())+"\n"); } } else{ retVal.append(0); } retVal.append(" Total number of bytes received = "); if (stats != null){ retVal.append(Utils.formatInt(stats.getTotalNumberOfReceivedBytes())); }else{ retVal.append(0); } // retVal.append(".\n"); //return retVal.toString(); return (stats==null) ? null : retVal.toString(); } /** Returns the number of input ports on this drain node. */ public int getNumberOfInputPorts(){ return inputPorts.length; } /** Returns the number of VLs per input port on this drain node. */ 234 public int getNumberOfVLs(){ return this.numberOfVLs; } /** Used to create stats when a packet is received.*/ private IBAPacket tmpPacket; private IBAInputPort[] inputPorts; private int numberOfVLs; /** Holds the state info on all VLs.*/ private State[][] states; private static final IBAPacket dummyPacket = new IBAPacket(); /** * Hold information on each incoming VL on all the incoming ports. */ private class State{ private static final int READY = 0; private static final int RECEIVING_HEAD = 1; private static final int RECEIVING_CONTENTS = 2; /** For storing incoming packets.*/ private ByteBuffer buffer; /** The LRHs of the currently incoming packets. */ private IBAPacket.LRH lrh; /** The number of bytes received from current packet. */ private int numBytesReceived; /** The number of bytes ready to be received from the input port. */ private int numBytesReadyToReceive; /** The states of the VL. */ private int state; protected State(){ init(); } private void init(){ buffer = new ByteBuffer(IBAPacket.MAX_SIZE); reset(); } private void reset(){ buffer.clear(); lrh = null; numBytesReceived = 0; state = READY; } } } 235 no.uio.ifi.gruns.infiniband.IBASourceNode package no.uio.ifi.gruns.infiniband; import no.uio.ifi.gruns.*; import no.uio.ifi.tools.*; public class IBASourceNode extends SourceNode implements IBAPacketSender{ public IBASourceNode(int numberOfOutputPorts, int numberOfVirtualLanes, Kernel kernel) { this(numberOfOutputPorts, numberOfVirtualLanes, "", kernel); } public IBASourceNode(int numberOfOutputPorts, int numberOfVirtualLanes, String ID, Kernel kernel){ super(numberOfOutputPorts, numberOfVirtualLanes, ID, kernel); init(numberOfOutputPorts, numberOfVirtualLanes); } /** * Common initialization */ private void init(int numberOfOutputPorts, int numberOfVirtualLanes){ addresses = new AddressVector(); routingTable = new IBARoutingTable(); outputPorts = new IBAOutputPort[numberOfOutputPorts]; for (int i=0; i < numberOfOutputPorts; i++){ outputPorts[i] = new IBAOutputPort(numberOfVirtualLanes, kernel); outputPorts[i].setID(ID+", output port "+i); } // transientThreshold = Integer.getInteger("infiniband.TRANSIENT_PERIOD", 0).intValue(); transientPeriod = true; numberOfPacketsSent = 0; numberOfPacketsWaiting = 0; reportedNumberOfPacketsSent = 0; reportedNumberOfPacketsWaiting = 0; SL = 0; VL = 0; /** Filler data for data packets */ StringBuffer buf = new StringBuffer(); for (int i=0; i < FILLER_SIZE; i++) { // Too slow // buf.append((char) ((int) ’A’ + random.nextInt(26))); buf.append(’A’); } fillData = buf.toString(); } 236 /** * Sets the address of this source node * @param The address as a string. */ public void setOwnAddress(String address){ this.address = new Address(address); } /** * Sets the address of this source node * @param The address as an Address object. */ public void setOwnAddress(Address address){ this.address = address; } /** * Sets the SL this source node sends packets on. * TODO: NOW IT SETS THE VL ALSO. FIX THIS. */ public void setSL(int SL){ this.SL = SL; this.VL = SL; } /** * Gets the SL this source node sends packets on. */ public void getSL(int SL){ this.SL = SL; } /** * Sets the inter-packet delay for this source node. * Controls how often this source node transmits packets. */ public void setInterPacketDelay(int delay){ this.interPacketDelay = delay; } /** * Sets how many percent of the bandwith in the network this source node * should use when sending packets. A value of 50 indicates that this source node * should try to send out packets so often that it uses about 50% of the bandwith * resources in the network. This value is computed from the forwarding capacity * of the units in the net, which is known a priori. The main unit of transfer in * the network is an IBA block, and each unit can process one IBA block per time step. * * From this, we can compute how long the inter packet delay must be for us * to utilize a certain percentage of the link capacity. * E.g. given a value of 50%, we can compute the delay like this: * <pre> * P -> Percentage of link usage * B -> Blocks per packet * D -> Delay in time steps (time spent sending one block) between each packet * * We then have the following: * * P = 100 * (B / (B + D)) * B / (B + D) = P / 100 * B = (P*(B + D))/100 * B - PB / 100 = PD / 100 * 237 * which gives us a delay of: * * D = (B * (100 - P)) / P * * </pre> */ public void setUsage(double percent){ int D; double P; // Percentage double B; // Time steps needed to send one packet. P = percent; // Floor so we don’t risk waiting too long. We need to keep the pipe full. // Better to throw away one packet because the last one isn’t finished // sending than not to send a packet when the link is free. B = (int) Math.floor((Double. parseDouble(System. getProperty("infiniband.EXP_PACKET_SIZE")) + 2) / Double.parseDouble(System.getProperty("infiniband.BLOCK_SIZE"))); // Inter-packet delay is just B if we are to send 100%. Don’t have to send more // than one packet at once. The other packets will just get thrown away. D = (int) B + (int) Math.round((B * (100 - P)) / P); this.interPacketDelay = D; // {{{ DEBUG /* Reporter.debugln(ID+": Set usage percentage to "+ P+" percent. Inter-packet delay set to "+D); */ // }}} } /** * Attaches a link to this source node on the * supplied output port number */ public void attach(Link l, int outputPortNumber) throws IllegalArgumentException { if (outputPorts == null){ throw new IllegalArgumentException("This source node "+ "has no output ports."); } if (outputPortNumber < 0 || outputPortNumber > outputPorts.length){ throw new IllegalArgumentException("Output port number out "+ "of range. Must be"+ "between 0 and "+ outputPorts.length); } 238 outputPorts[outputPortNumber].attach(l); // Update the ID to be able to distinguish outputports // from each other outputPorts[outputPortNumber].setID(ID+", port "+ outputPortNumber); } /** * Adds a new address that this source node can send to * @param The address as a string. */ public void addDestinationAddress(String address){ this.addresses.add(new Address(address)); } /** * Adds an address range that this source node can send * to. The format of the string can be one of the following, * where aaa, bbb, ccc and ddd represents integers between 0 and 255: * <table> * <tr><td>aaa.bbb</td><td>Adds the specified address</tr> * <tr><td>aaa.bbb-ccc.ddd</td><td>Adds addresses ranging from * aaa.bbb to ccc.ddd, inclusive.</td></tr> * </table> * @param range A string describing the address range. */ public void addDestinationAddresses(String range) throws IllegalArgumentException{ int dashIndex = range.indexOf(’-’); // No range specified, just add the address if (dashIndex == -1){ addDestinationAddress(range); } // Fetch the addresses from the sring supplied Address[] addrs = getAddresses(range); // Add all the addresses for (int i=0; i < addrs.length; i++){ addresses.add(addrs[i]); } } /** * Adds an output port to which packets addresses to the * specified addresses can be sent. * Same address format as addDestinationAddress. */ public void addRoutingInterval(String addresses, int outputPortNumber){ Address[] addrs = getAddresses(addresses); for (int i=0; i < addrs.length; i++){ routingTable.addRoute(addrs[i], outputPortNumber); } } /** * Deletes an output port to which packets addresses to the * specified addresses can be sent. * Same address format as addDestinationAddress. 239 */ public void deleteRoutingInterval(String addresses, int outputPortNumber){ Address[] addrs = getAddresses(addresses); for (int i=0; i < addrs.length; i++){ routingTable.deleteRoute(addrs[i], outputPortNumber); } } /** * Sets the number of units we can send from high priority * before letting a unit from low priority send. * @param portNumber The port number to set the limit for * @param limit The number of units */ public void setLimitOfHighPriority(int portNumber, int limit){ outputPorts[portNumber].setLimitOfHighPriority(limit); } /** * Adds an entry to the high priority list of this source node. * Means that port number <tt>portNumber</tt> is allowed to send * <tt>weight</tt> units on virtual lane number <tt>VLNumber</tt>. * This entry has high priority, which means that it will be selected * before any of the entries in the low priority table. */ public void addHighPriority(int portNumber, int VLNumber, int weight){ outputPorts[portNumber].addHighPriority(VLNumber, weight); } /** * Adds an entry to the low priority list of this source node. * Means that port number <tt>portNumber</tt> is allowed to send * <tt>weight</tt> units on virtual lane number <tt>VLNumber</tt>. * This entry has low priority, which means that it will be selected * after any of the entries in the high priority table. */ public void addLowPriority(int portNumber, int VLNumber, int weight){ outputPorts[portNumber].addLowPriority(VLNumber, weight); } /** * Checks an address on string form for validity */ private void checkAddress(String address) throws IllegalArgumentException { int[] addr = { Integer.parseInt(address. substring(0, address.indexOf(’.’))), Integer.parseInt(address. substring(address.indexOf(’.’)+1)) } ; if (addr[0] < 0 || addr[0] > 255){ throw new IllegalArgumentException("Address parts must be "+ "in the range 0 to 255. "+ addr[0]+ " is out of range."); } if (addr[1] < 0 || addr[1] > 255){ throw new IllegalArgumentException("Address parts must be "+ 240 "in the range 0 to 255. "+ addr[1]+ " is out of range."); } } /** * Parses a string with an address range and returns an array * containing each address in the range. * The format of the string can be one of the following, * where aaa, bbb, ccc and ddd represents integers between 0 and 255: * <table> * <tr><td>aaa.bbb</td><td>Adds the specified address</tr> * <tr><td>aaa.bbb-ccc.ddd</td><td>Adds addresses ranging from * aaa.bbb to ccc.ddd, inclusive.</td></tr> * </table> * @param range A string describing the address range. */ private Address[] getAddresses(String range){ AddressVector retVal = new AddressVector(); int dashIndex = range.indexOf(’-’); // No range specified, just add the address if (dashIndex == -1){ Address[] a = { new Address(range)}; return a; } else{ String startAddr = range.substring(0, dashIndex); String endAddr = range.substring(dashIndex+1); int[] startAddress = { Integer.parseInt(startAddr. substring(0, range.indexOf(’.’))), Integer.parseInt(startAddr. substring(range.indexOf(’.’)+1)) } ; int[] endAddress = { Integer.parseInt(endAddr. substring(0, range.indexOf(’.’))), Integer.parseInt(endAddr. substring(range.indexOf(’.’)+1)) } ; int[] currentAddress = new int[2]; // Check the input parameters // Range check checkAddress(startAddr); checkAddress(endAddr); // Start must be smaller than end if ( (endAddress[0] < startAddress[0]) || ( (endAddress[0] == startAddress[0]) && (endAddress[1] < startAddress[1]) ) ) { throw new IllegalArgumentException("\nStart address "+ "must be smaller than"+ " end address. "+ startAddr+ " is larger than "+ endAddr+"."); } // Loop through all the addresses and add them 241 int limit = 255; currentAddress[1] = startAddress[1]; for (currentAddress[0] = startAddress[0]; currentAddress[0] <= endAddress[0]; currentAddress[0]++){ // Adjust the limit of the last part of the address if // the last prefix is reached if (currentAddress[0] == endAddress[0]){ limit = endAddress[1]; } while( currentAddress[1] <= limit ){ retVal.add(new Address(currentAddress[0]+"." +currentAddress[1])); currentAddress[1]++; } // Reset last part of address for next run currentAddress[1] = 0; } } return retVal.toAddressArray(); } public void sendIBAPacket(IBAPacket p, int portNumber, int VLNumber){ if (outputPorts[portNumber]. canSend(VLNumber, p.getSize())){ outputPorts[portNumber].sendIBAPacket(p, VLNumber); // Schedule the output port so that the packet moves on kernel.schedule(outputPorts[portNumber], kernel.currentTime() + 1); numberOfPacketsSent++; numberOfPacketsWaiting--; // Check to see if the transient period is over yet if (transientPeriod) { if (kernel.currentTime() > transientThreshold) { transientPeriod = false; } } else { reportedNumberOfPacketsSent++; reportedNumberOfPacketsWaiting--; } // // // if For extra checking. act() fails abruptly if the number of waiting packets is more than zero and packet is null. (numberOfPacketsWaiting == 0) { this.packet = null; } /* // DEBUG 242 Reporter.debugln(ID+": Sending packet on port "+portNumber+", "+ "waiting packets = "+numberOfPacketsWaiting); // */ } else{ //IBAStatistics.reportDroppedPacket(this); /* // DEBUG Reporter.debugln(ID+": Can’t send packet on port "+portNumber+", "+ "waiting packets = "+numberOfPacketsWaiting); // */ } } /** * Makes an IBA packet */ protected IBAPacket makeIBAPacket(){ // Fetch a random address from our addresses Address destination = RandomAddress.nextAddress(addresses.toAddressArray()); // Make a packet to the specified address IBAPacket p = new IBAPacket(destination, this.address); // Tell that this packet is a local packet p.lrh.setLNH(IBAPacket.LNH_IBA_local); // Make this a "Send Only" packet. p.bth.setOpCode(IBAPacket.RD_SEND_ONLY); p.lrh.setVL(this.VL); p.lrh.setSL(this.SL); // Must know ecact payload length, so skip this message. //StringBuffer str = new StringBuffer("Hello World! Test message "+ // "from source node "+ // this.ID+". "); StringBuffer str = new StringBuffer(); // Fill up with a random number of random characters // int numChars = random.nextInt(2000); // Use fixed number instead, for predicatability int numChars = Integer.parseInt(System.getProperty("infiniband.EXP_PAYLOAD_SIZE")); // Too slow //for (int i=0; i < numChars; i++) { // str.append(’A’); //} // Much faster str.append(fillData.substring(0, numChars)); p.pyld.setContent(str.toString().getBytes()); p.updatePacketLength(); 243 // Update the number of packets created but not yet sent numberOfPacketsWaiting++; if (transientPeriod) { if (kernel.currentTime() > transientThreshold) { transientPeriod = false; } }else { reportedNumberOfPacketsWaiting++; } return p; } public void act(){ Address a = null; Route route = null; int outputPort = 0; // Make a new packet each "interPacketDelay" time steps. if (kernel.currentTime() % this.interPacketDelay == 0) { packet = makeIBAPacket(); } if (numberOfPacketsWaiting > 0) { if (packet == null) { throw new RuntimeException("IBASourceNode.act: ERROR! "+ " packet == null!"); } a = new Address(packet.lrh.DLID()); route = routingTable.lookUp(a); outputPort = 0; if (route !=null){ outputPort = route.portNumber; }else{ Reporter.error("IBASourceNode.act(): "+ "No route found for supplied address:"); Reporter.error(a.getAddressAsString()+" "+ "("+Utils.asBitString(a.getAddress())+")"); } sendIBAPacket(packet, outputPort, packet.lrh.VL()); } // Reschedule in "interPacketDelay" time steps kernel.schedule(this, kernel.currentTime() + /*random.nextInt(2*this.interPacketDelay)+*/ this.interPacketDelay); // Reschedule on next time step if we have more data to send. if (this.numberOfPacketsWaiting > 0) { kernel.schedule(this, kernel.currentTime() + 1); } } /** * Returns the number of packets sent successfully. */ public int getSentPackets() { 244 return reportedNumberOfPacketsSent; } /** * returns the number of packets waiting to be sent. */ public int getWaitingPackets() { return reportedNumberOfPacketsWaiting; } public String report(){ return "IBASourceNode "+this.ID+ ": Packets sent = "+ Utils.formatInt(reportedNumberOfPacketsSent)+ /* ", packets dropped = "+ Utils.formatInt(IBAStatistics.getDroppedPacketStats(this) */ ", packets waiting to be sent = "+ Utils.formatInt(reportedNumberOfPacketsWaiting) ; } /** A routing table to know where to send outbound packets */ protected IBARoutingTable routingTable; /** The output ports to send to */ protected IBAOutputPort[] outputPorts; /** Used to draw different times to reschedule */ protected java.util.Random random = new java.util.Random(); /** The delay between packets being sent out.*/ protected int interPacketDelay; /** Number of packets send out on the network */ protected int numberOfPacketsSent; /** Number of created packets that are waiting to be sent out on the network */ protected int numberOfPacketsWaiting; /** * Reported number of packets send out on the network. Not registered * before the end of the transient period. */ protected int reportedNumberOfPacketsSent; /** Reported number of created packets that are waiting to be sent out on the network. * Not registered before the end of the transient period. */ protected int reportedNumberOfPacketsWaiting; /** The SL this source node is sending packets on */ protected int SL; /** The VL this source node is sending packets on */ protected int VL; /** Size of filler packet */ protected static final int FILLER_SIZE = 100000; /** The next packet to send */ 245 protected IBAPacket packet; /** Used to add stochastic filler data to packets, and avoid creating the data on each time step. */ protected String fillData; /** * Indicates when the transient period is over, * so we should start gathering data. */ protected int transientThreshold; /** * Indicates that the transient period is over */ protected boolean transientPeriod; } 246 no.uio.ifi.gruns.infiniband.NotClearToSendException package no.uio.ifi.gruns.infiniband; /** * Thrown when trying to send to an output port on a VL * which the output port is currently receiving a packet on. * Then there won’t be any mixing of the two packets. */ public class NotClearToSendException extends RuntimeException{ public NotClearToSendException(){ super(); } public NotClearToSendException(String message){ super(message); } } 247 no.uio.ifi.tools.Comparator package no.uio.ifi.tools; /** * Used to compare two objects of any type, just * define the methods accordingly. */ public interface Comparator{ /** * True if and only if <i>a</i> is less than <i>b</i> */ public boolean isLessThan(Object a, Object b); /** * True if and only if <i>a</i> is less than or equal to <i>b</i> */ public boolean isLessThanOrEqualTo(Object a, Object b); /** * True if and only if <i>a</i> and <i>b</i> are equal */ public boolean isEqualTo(Object a, Object b); /** * True if and only if <i>a</i> is greater than <i>b</i> */ public boolean isGreaterThan(Object a, Object b); /** * True if and only if <i>a</i> is greater than or equal to <i>b</i> */ public boolean isGreaterThanOrEqualTo(Object a, Object b); /** * True if and only if <i>a</i> can be compared */ public boolean isComparable(Object a); } 248 no.uio.ifi.tools.Item package no.uio.ifi.tools; /** * Stores a (key, value) pair */ public class Item { private Object key, element; public Item(Object key, Object element){ this.key = key; this.element = element; } public Object getKey() { return key; } public Object getElement(){ return element; } public void setKey(Object key){ this.key = key; } public void setElement(Object element) { this.element = element; } } 249 no.uio.ifi.tools.ItemComparator package no.uio.ifi.tools; /** * <p> * Compares two {@link no.uio.ifi.gruns.Item}s. * Uses the <tt>no.uio.ifi.tools.Item.key</tt> value to compare. * </p> * <p> * It depends on that the value of the key of an * {@link no.uio.ifi.gruns.Item} is an {@link java.lang.Integer} */ public class ItemComparator implements Comparator{ public boolean isLessThan(Object a, Object b){ int aVal, bVal; aVal = ( (Integer)((Item) a).getKey() ).intValue(); bVal = ( (Integer)((Item) b).getKey() ).intValue(); return ( aVal < bVal ); } public boolean isLessThanOrEqualTo(Object a, Object b){ int aVal, bVal; aVal = ( (Integer)((Item) a).getKey() ).intValue(); bVal = ( (Integer)((Item) b).getKey() ).intValue(); return ( aVal <= bVal ); } public boolean isEqualTo(Object a, Object b){ int aVal, bVal; aVal = ( (Integer)((Item) a).getKey() ).intValue(); bVal = ( (Integer)((Item) b).getKey() ).intValue(); return ( aVal == bVal ); } public boolean isGreaterThan(Object a, Object b){ int aVal, bVal; aVal = ( (Integer)((Item) a).getKey() ).intValue(); bVal = ( (Integer)((Item) b).getKey() ).intValue(); return ( aVal > bVal ); } public boolean isGreaterThanOrEqualTo(Object a, Object b){ int aVal, bVal; aVal = ( (Integer)((Item) a).getKey() ).intValue(); bVal = ( (Integer)((Item) b).getKey() ).intValue(); return ( aVal >= bVal ); } public boolean isComparable(Object a){ 250 if (a==null) return false; try{ Integer integer = (Integer) ( (Item) a ).getKey(); }catch (ClassCastException e) { return false; } return true; } } 251 no.uio.ifi.tools.JavaPriorityQueue package no.uio.ifi.tools; import java.io.Serializable; import java.util.*; /** * A Priority Queue based on the Java Collections Framework. * Based on the JDC Core Java Technologies Tech Tips August 21, 2002. * * Extended to be dynamic, e.g. one can have a priority queue with * the priorities 3, 40 and 500, and suddely you can add an item at * priority 2 or 37 or 349 or 504. This fills the needs of the kernel * in the gruns simulator. */ public class JavaPriorityQueue extends AbstractList implements Serializable { //private final static int DEFAULT_PRIORITY_COUNT = 10; private final static int DEFAULT_PRIORITY = 0; // private List queue[]; private LinkedList queue; public JavaPriorityQueue() { this(null); } public JavaPriorityQueue(Collection col) { queue = new LinkedList(); if (col != null) { addAll(col); } } public boolean add(Object element) { insert(element, DEFAULT_PRIORITY); return true; } /** * Inserts the object specified at the priority supplied. * Doesn’t insert an object more than once on the same priority, * so the second call here would have no effect: *<pre> * queue.insert("Joy", 8); * queue.insert("Joy", 8); *</pre> * Of course, one can add the same element more than once using * different priorities. */ public void insert(Object element, int priority) { if (priority < 0) { throw new IllegalArgumentException( "Illegal priority: " + priority); 252 } // Find position to insert element on. // It’s probably the last position in the simulator kernel, // so we traverse the queue backwards looking for the right spot // to insert. PriorityItem[] prios = new PriorityItem[queue.size()]; PriorityItem prio = null; prios = (PriorityItem[]) queue.toArray(prios); // // // if Check to see if the priority is bigger (i.e. less important) than the last entry. If it is, just add a new item at the end. Also if the list contains no items. ( queue.size() == 0 || ((PriorityItem)queue.getLast()).priority < priority) { prio = new PriorityItem(priority); queue.add(prio); } // If the first item is bigger thatn this one, insert first in queue. else if ( ((PriorityItem)queue.getFirst()).priority > priority) { //System.err.println("first.priority = "+ // ((PriorityItem)queue.getFirst()).priority+ // ", priority = "+priority); prio = new PriorityItem(priority); queue.addFirst(prio); } // If not, find the right spot to insert the item. else{ for (int i=prios.length-1; i >=0; i--) { //System.err.print("prios["+i+"].priority == "+ // prios[i].priority+", "+ // "priority == "+priority); if (prios[i].priority==priority) { //System.err.println("...found priority!"); // Found the priority in the queue. prio = prios[i]; break; } else if (prios[i].priority < priority) { //System.err.println("...did not find priority!"); // Priority not found in queue. Make a new // entry below the nearest one bigger than this. prio = new PriorityItem(priority); queue.add(i+1, prio); break; } } } //System.err.println("queue = "+this); //System.out.println("prio = "+prio); // if the item is already present at that priority, don’t add it. if (prio.items.indexOf(element) == -1) { prio.items.add(element); } //System.out.println("prio = "+prio); //System.err.println("queue = "+this); modCount++; } 253 public Object getFirst() { return iterator().next(); } public Object get(int index) { if (index < 0) { throw new IllegalArgumentException( "Illegal index: "+ index); } Iterator iter = iterator(); int pos = 0; while (iter.hasNext()) { Object obj = iter.next(); if (pos == index) { return obj; } else { pos++; } } return null; } public void clear() { PriorityItem pItem; Iterator iter = queue.iterator(); // Remove all the elements on each priority while(iter.hasNext()) { pItem = (PriorityItem)iter.next(); if (pItem != null) { pItem.items.clear(); } } // Remove the list of priorities queue.clear(); } public Object removeFirst() { Iterator iter = iterator(); Object obj = iter.next(); iter.remove(); if ( ((PriorityItem)queue.getFirst()).items.size() == 0) { queue.removeFirst(); } return obj; } /** * Returns the value of the most important (lowest priority) * item in the queue */ public int minValue() { if (queue.size() > 0) { PriorityItem item = (PriorityItem)queue.getFirst(); return item.priority; } 254 else return -20; } public int size() { PriorityItem pItem; int size = 0; Iterator iter = queue.iterator(); while(iter.hasNext()) { pItem = (PriorityItem)iter.next(); if (pItem != null) { size += pItem.items.size(); } } return size; } /** * The priorities are soneqhat reversed, since I want the item on the * lowest time step to be picked first, not the one on the highest time step. */ public Iterator iterator() { Iterator iter = new Iterator() { int expectedModCount = modCount; int priority = queue.size() - 1; int count = 0; int size = size(); // Used to prevent successive remove() calls int lastRet = -1; Iterator tempIter; // Get iterator for highest (smallest) priority { if (queue.size() == 0){ tempIter = null; } else { tempIter = ((PriorityItem)queue.getFirst()).items.iterator(); } /* if (queue[priority] == null) { tempIter = null; } else { tempIter = queue[priority].iterator(); } */ } private final void checkForComodification() { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } public boolean hasNext() { return count != size(); } 255 public Object next() { checkForComodification(); if (tempIter == null) { throw new NoSuchElementException(); } else { lastRet = count++; return tempIter.next(); } } public void remove() { if (lastRet == -1) { throw new IllegalStateException(); } checkForComodification(); tempIter.remove(); count--; lastRet = -1; expectedModCount = modCount; } }; return iter; } public String toString() { StringBuffer buffer = new StringBuffer("{"); Iterator iter = queue.iterator(); PriorityItem pItem; int i = 0; while (iter.hasNext()) { pItem = (PriorityItem)iter.next(); if (i > 0){ buffer.append(","); } buffer.append(pItem.priority+":"); buffer.append(pItem.items.toString()); ++i; } buffer.append("}"); return buffer.toString(); } /** * Holds a set of (int priority, LinkedList items). * E. g. in the gruns simulator, we would use this to hold * a time step and a list of units that is to act on this time step. */ protected class PriorityItem { protected int priority; protected LinkedList items; protected PriorityItem(int priority) { this.priority = priority; items = new LinkedList(); } public String toString() { 256 return priority+":"+items.toString(); } } } 257 no.uio.ifi.tools.PriorityQueue package no.uio.ifi.tools; public interface PriorityQueue{ /** Returns the number of elements in this priority queue */ public int getSize(); /** Tests whether this priority queue is empty */ public boolean isEmpty(); /** * Inserts a new element * @param key The key to access the element with (often an Integer) * @param element The element to insert */ public void insertItem(Object key, Object element); /** * Returns (but does not remove) an element of this ordered * queue with the smallest key. The element is not removed. */ public Object getMinElement(); /** * Returns the smallest key in this priority queue. */ public Object getMinKey(); /** * Removes an element with the smallest key. */ public Object removeMinElement(); /** * States whether the element supplied is * currently in the priority queue or not. */ public boolean contains(Object o); /** * States whether the element supplied is * currently in the priority queue with the supplied int as a * key, or not. */ public boolean contains(int key, Object o); /** * Returns a string representation of the priority queue * (An ordered list of the elements in the queue) */ public String toString(); } 258 no.uio.ifi.tools.SimplePriorityQueue package no.uio.ifi.tools; import java.util.ArrayList; import java.util.Arrays; /** * A very basic implementation of an priority queue, using * an array to store the elements. Could be transformed * later to e.g. use a heap instead, for performance reasons */ public class SimplePriorityQueue implements PriorityQueue { /** A list containing all the elements */ protected ArrayList elements; /** The object used to compare the elements in the list */ protected Comparator comparator; public SimplePriorityQueue(Comparator comparator){ this.comparator = comparator; this.elements = new ArrayList(); } public int getSize(){ return elements.size(); } public boolean isEmpty(){ return elements.isEmpty(); } public void insertItem(Object key, Object element){ elements.add(new Item(key, element)); // Sort the list (just as fast, and more intuitive) Item[] items = new Item[elements.size()]; elements.toArray(items); Arrays.sort(items, new java.util.Comparator(){ public int compare(Object o1, Object o2){ int i1 = ((Integer) ((Item)o1).getKey()).intValue(); int i2 = ((Integer) ((Item)o2).getKey()).intValue(); if (i1 < i2) return -1; if (i1 == i2) return 0; if (i1 > i2) return 1; return -17; // ERROR } public boolean equals(Object o){ return false; } }); for (int i=0; i < items.length; i++){ elements.set(i, items[i]); } 259 } public Object getMinElement(){ // Return null if no elements in the queue if (isEmpty()){ return null; } // Be sure that the list is sorted if you just use this! return ((Item) elements.get(0)).getElement(); // // // This works if the list is unsorted as well Object item1 = elements.get(0); Object item2; // // // // // // // // // Loop through all the objects and find the smallest one for (int i=1; i < elements.size(); i++) { item2 = elements.get(i); if (comparator.isLessThan(item2, item1)){ item1 = item2; } } return ((Item) item1).getElement(); } public Object getMinKey(){ // Return null if no elements in the queue if (isEmpty()){ return null; } // Be sure that the list is sorted if you just use this! return ((Item) elements.get(0)).getKey(); // // // This works if the list is unsorted as well Object item1 = elements.get(0); Object item2; // // // // // // // // // Loop through all the objects and find the smallest one for (int i=1; i < elements.size(); i++) { item2 = elements.get(i); if (comparator.isLessThan(item2, item1)){ item1 = item2; } } return ((Item) item1).getKey(); } public Object removeMinElement(){ // Return null if no elements in the queue if (isEmpty()){ return null; } // Be sure that the list is sorted if you just use this! Object item = elements.get(0); 260 elements.remove(0); return ((Item) item).getElement(); // // // This works if the list is unsorted as well Object item1 = elements.get(0); Object item2; // // // // // // // // Loop through all the objects and find the smallest one for (int i=1; i < elements.size(); i++) { item2 = elements.get(i); if (comparator.isLessThan(item2, item1)){ item1 = item2; } } // // // remove the Item from the elements list elements.remove(elements.indexOf(item1)); // // // Return the element return ((Item) item1).getElement(); } public boolean contains(Object o){ Item item; for (int i=0; i < elements.size(); i++) { item = (Item) elements.get(i); if (((Item) item).getElement() == o){ return true; } } return false; } public boolean contains(int key, Object o){ Item item; for (int i=0; i < elements.size(); i++) { item = (Item) elements.get(i); if (item.getElement() == o && ((Integer)item.getKey()).intValue() == key ){ return true; } } return false; } public String toString(){ /* Breaks generality StringBuffer retString = new StringBuffer(); Item tmpItem; Reporting tmpUnit; retString.append("[\n"); for (int i=0; i < elements.size(); i++){ tmpItem = (Item) elements.get(i); tmpUnit = (Reporting) tmpItem.getElement(); retString.append(" [Item: \""+tmpUnit.report()+ 261 "\", scheduled at: "+tmpItem.getKey()+ "]\n"); } retString.append("]"); return retString.toString(); */ return "SimplePriorityQueue"; } } 262