package com.sun.solaris.domain.pools;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import java.util.regex.*;
import java.text.DecimalFormat;
import com.sun.solaris.service.locality.*;
import com.sun.solaris.service.logging.*;
import com.sun.solaris.service.pools.*;
import com.sun.solaris.service.exception.*;
class SystemSolver implements Solver {
public static final String DH_FILE_DEF_PATH =
"/var/adm/pool/history";
public static final String DH_FILE_PROP_NAME =
"system.poold.history-file";
private LocalityDomain ldom;
private Map objMap;
private static final Pattern p0 = Pattern.compile(";");
private Configuration conf;
private final Monitor monitor;
private DecisionHistory dh;
private String dhPath;
private int cpuCount;
private int examineCount;
SystemSolver(Monitor monitor)
{
objMap = new HashMap();
this.monitor = monitor;
}
public void initialize(Configuration conf) throws PoolsException
{
String oString = null;
String exps[];
this.conf = conf;
cpuCount = conf.getComponents(null).size();
examineCount = 0;
objMap.clear();
try {
oString = conf.getStringProperty(
"system.poold.objectives");
if (oString.length() > 0) {
Set oSet = new HashSet();
objMap.put(conf, oSet);
Poold.CONF_LOG.log(Severity.DEBUG,
"adding configuration objective " +
oString);
exps = p0.split(oString);
for (int i = 0; i < exps.length; i++) {
try {
Expression exp =
Expression.valueOf(
exps[i]);
addObjective(oSet, "system",
exp);
} catch (IllegalArgumentException iae) {
Poold.utility.warn(
Poold.CONF_LOG, iae, false);
}
}
}
} catch (PoolsException pe) {
}
Value typeProp = new Value("type", "pset");
List valueList = new LinkedList();
valueList.add(typeProp);
Iterator resIt = conf.getResources(valueList).iterator();
typeProp.close();
while (resIt.hasNext()) {
Resource resource = (Resource)resIt.next();
try {
oString = resource.getStringProperty(
"pset.poold.objectives");
if (oString.length() > 0) {
Set oSet = new HashSet();
objMap.put(resource, oSet);
Poold.CONF_LOG.log(Severity.DEBUG,
"adding " +
resource.getStringProperty(
"pset.name") +
" objective \"" + oString + "\"");
exps = p0.split(oString);
for (int i = 0; i < exps.length; i++) {
Expression exp = null;
try {
exp = Expression.
valueOf(exps[i]);
addObjective(oSet,
"pset", exp);
} catch
(IllegalArgumentException e)
{
Poold.utility.warn(
Poold.CONF_LOG, e,
false);
}
}
}
} catch (PoolsException pe) {
continue;
}
}
Poold.CONF_LOG.log(Severity.DEBUG, "objective map: " +
objMap.toString());
if (ldom != null) {
ldom.close();
}
try {
ldom = new LocalityDomain(LocalityDomain.LGRP_VIEW_OS);
} catch (Exception e) {
Poold.utility.die(Poold.OPT_LOG, e);
}
String newDhPath;
try {
newDhPath = conf.getStringProperty(
DH_FILE_PROP_NAME);
} catch (PoolsException pe) {
newDhPath = DH_FILE_DEF_PATH;
}
if (!newDhPath.equals(dhPath)) {
try {
dh = DecisionHistory.loadFromFile(newDhPath);
Poold.CONF_LOG.log(Severity.DEBUG,
"loaded history file " + newDhPath);
} catch (Exception e) {
if (!(e instanceof FileNotFoundException)) {
Poold.CONF_LOG.log(Severity.WARNING,
newDhPath +
": contents unusable; ignoring");
Poold.CONF_LOG.log(Severity.DEBUG,
newDhPath + ": contents unusable",
e);
}
if (dh == null)
dh = new DecisionHistory();
}
try {
dh.syncToFile(newDhPath);
} catch (Exception e) {
Poold.utility.die(Poold.CONF_LOG,
new PooldException(newDhPath +
": couldn't synchronize history file")
.initCause(e), false);
}
dhPath = newDhPath;
}
}
private boolean hasNonWorkloadDependentObjectives(Element elem)
{
Set elemObj = (Set)objMap.get(elem);
if (elemObj == null)
return (false);
Iterator elemObjIt = elemObj.iterator();
while (elemObjIt.hasNext())
if (elemObjIt.next() instanceof
WorkloadDependentObjective)
return (false);
return (true);
}
private boolean hasWorkloadDependentObjectives(Element elem)
{
Set elemObj = (Set)objMap.get(elem);
if (elemObj == null)
return (false);
Iterator elemObjIt = elemObj.iterator();
while (elemObjIt.hasNext())
if (elemObjIt.next() instanceof
WorkloadDependentObjective)
return (true);
return (false);
}
public boolean examine(Monitor mon) throws PoolsException,
StaleMonitorException
{
dh.expireAndMeasureImprovements(mon);
Iterator objIt = objMap.keySet().iterator();
boolean ret = false;
boolean hasLocalityObjectives = false;
boolean isMonitorValid = true;
while (objIt.hasNext()) {
Element elem = (Element) objIt.next();
Set elemObj = (Set) objMap.get(elem);
Iterator elemObjIt = elemObj.iterator();
while (elemObjIt.hasNext()) {
Objective obj = (Objective) elemObjIt.next();
Poold.OPT_LOG.log(Severity.DEBUG,
"checking objective " + obj);
if (obj instanceof WorkloadDependentObjective) {
if (isValid()) {
ret = ((WorkloadDependentObjective)
obj).examine(conf, this, elem) ||
ret;
} else
isMonitorValid = false;
}
}
if (!hasLocalityObjectives &&
hasNonWorkloadDependentObjectives(elem))
hasLocalityObjectives = true;
}
if (isMonitorValid == false) {
Poold.MON_LOG.log(Severity.INFO,
"not evaluating workload-dependent objectives " +
"until sufficient statistics are collected");
}
return (ret || (hasLocalityObjectives && (examineCount++ <
cpuCount / 2)));
}
public boolean solve() throws Exception
{
boolean madeMove = false;
Configuration rwConf = new Configuration(conf.getLocation(),
PoolInternal.PO_RDWR);
try {
Map resImp = new HashMap();
List poolList = rwConf.getPools(null);
Iterator itPool = poolList.iterator();
while (itPool.hasNext()) {
Pool pool = (Pool) itPool.next();
long newImp = pool.
getLongProperty("pool.importance");
List resList = pool.getResources(null);
Iterator itRes = resList.iterator();
while (itRes.hasNext()) {
Resource res = (Resource) itRes.next();
if (resImp.containsKey(res)) {
Long imp = (Long) resImp.
get(res);
if (newImp > imp.longValue())
resImp.put(res,
Long.valueOf(
newImp));
} else
resImp.put(res,
Long.valueOf(newImp));
}
}
Value val = new Value("type", "pset");
List valueList = new LinkedList();
valueList.add(val);
List resList = rwConf.getResources(valueList);
val.close();
List donors = getDonors(resList);
List receivers = getRecipients(resList);
Poold.OPT_LOG.log(Severity.DEBUG, "donors: " +
donors);
Poold.OPT_LOG.log(Severity.DEBUG, "receivers: " +
receivers);
Iterator itDonor = donors.iterator();
List moves = new ArrayList();
while (itDonor.hasNext()) {
Resource donor = (Resource) itDonor.next();
List processors = getProcessors(donor);
Poold.OPT_LOG.log(Severity.DEBUG,
"donor processors: " + processors);
Iterator itProcessor = processors.iterator();
while (itProcessor.hasNext()) {
Component cpu = (Component) itProcessor.
next();
Iterator itReceiver = receivers.
iterator();
while (itReceiver.hasNext()) {
Resource receiver =
(Resource) itReceiver.
next();
if (receiver == donor)
continue;
moves.add(new ComponentMove(
donor, receiver, cpu));
}
}
}
Poold.OPT_LOG.log(Severity.DEBUG,
"potential moves: " + moves);
HashSet scores = new HashSet();
Iterator itMoves = moves.iterator();
while (itMoves.hasNext()) {
double totalContrib = 0;
Move move = (Move) itMoves.next();
Iterator objIt = objMap.keySet().iterator();
while (objIt.hasNext()) {
Element elem = (Element) objIt.next();
Set elemObj = (Set) objMap.get(elem);
Iterator elemObjIt = elemObj.iterator();
while (elemObjIt.hasNext()) {
Objective obj =
(Objective)elemObjIt.next();
if (obj instanceof
LocalityObjective)
((LocalityObjective)obj)
.prepare(ldom,
(Resource)elem);
if (obj instanceof
WorkloadDependentObjective)
if (!isValid())
continue;
double contrib = obj.calculate(
rwConf, move, elem);
if (contrib < -1 || contrib > 1)
throw new
IllegalOFValueException(
"x: " + contrib +
" is invalid, legal " +
"range is -1 <= x <= " +
"1");
if (resImp.containsKey(elem)) {
contrib *= ((Long)
resImp.get(elem)).
longValue();
}
totalContrib += contrib *
obj.getExpression().
getImportance();
}
}
Poold.OPT_LOG.log(Severity.DEBUG,
"scored move (" + move + ") " +
totalContrib);
scores.add(new ScoreMove(move, totalContrib));
}
if (scores.size() != 0) {
Object scoresArray[] = scores.toArray();
Arrays.sort(scoresArray,
Collections.reverseOrder());
if ((madeMove = processMoves(rwConf,
scoresArray, false)) == false)
madeMove = processMoves(rwConf,
scoresArray, true);
} else
Poold.OPT_LOG.log(Severity.INFO,
"no moves found");
rwConf.close();
Poold.OPT_LOG.log(Severity.DEBUG,
"synchronizing decision history");
dh.syncToFile(dhPath);
} catch (Exception ex) {
rwConf.close();
throw ex;
}
return (madeMove);
}
private boolean processMoves(Configuration rwConf, Object scores[],
boolean ignoreDH) throws PoolsException, StaleMonitorException
{
boolean madeMove = false;
for (int i = 0; i < scores.length; i++) {
ScoreMove move = (ScoreMove) scores[i];
if (move.getScore() <= 0) {
if (ignoreDH)
Poold.OPT_LOG.log(Severity.INFO,
move + " not applied as " +
"benefit not significant");
break;
}
if ((madeMove = applyMove(rwConf, move, ignoreDH)) ==
true)
break;
}
return (madeMove);
}
private boolean applyMove(Configuration conf, ScoreMove move,
boolean ignoreDH)
throws PoolsException, StaleMonitorException
{
boolean madeMove = false;
boolean wdpInvolved = false;
double utilization = 0.0;
Poold.OPT_LOG.log(Severity.DEBUG, "selected " + move);
if (hasWorkloadDependentObjectives(move.getMove().getTo()) ||
hasWorkloadDependentObjectives(conf)) {
Poold.OPT_LOG.log(Severity.DEBUG,
"Attempting to retrieve utilization for:" + move
.getMove().getTo());
utilization = monitor.getUtilization(move
.getMove().getTo());
wdpInvolved = true;
}
if (ignoreDH || !wdpInvolved || !dh.veto(move.getMove(),
utilization)) {
Poold.OPT_LOG.log(Severity.INFO,
"applying move " + move.getMove());
move.getMove().apply();
ResourceMonitor mon = getMonitor().get(move.getMove().
getFrom());
mon.resetData("utilization");
mon = getMonitor().get(move.getMove().getTo());
mon.resetData("utilization");
try {
Poold.OPT_LOG.log(Severity.DEBUG,
"committing configuration");
conf.commit(0);
try {
if (move.getMove() instanceof
ComponentMove)
if (wdpInvolved)
dh.recordProcessorMove(
(ComponentMove)move
.getMove(),
utilization,
monitor
.getSampleCount());
else
Poold.OPT_LOG.log(
Severity.DEBUG,
"decision not " +
"recorded due to " +
"lack of workload-"
+ "dependent " +
"objectives");
} catch (Exception e) {
Poold.OPT_LOG.log(Severity.INFO,
"couldn't update " +
"decision history (" +
e.toString() + ")");
}
madeMove = true;
} catch (PoolsException pe) {
conf.rollback();
Poold.OPT_LOG.log(Severity.INFO,
"move failed, possibly due to a " +
"bound process in a 1-processor " +
"set");
}
} else {
if (!ignoreDH && wdpInvolved)
Poold.OPT_LOG.log(Severity.INFO,
move.getMove() + " not applied due to " +
"poor past results");
}
return (madeMove);
}
private void addObjective(Set oSet, String type, Expression exp)
throws IllegalArgumentException
{
Objective o = AbstractObjective.getInstance(type, exp);
Poold.CONF_LOG.log(Severity.DEBUG, "parsed objective " + o);
Iterator itObjs = oSet.iterator();
while (itObjs.hasNext()) {
Objective other = (Objective) itObjs.next();
if (o.getExpression().contradicts(
other.getExpression()))
throw new IllegalArgumentException(
"contradictory objectives:" + other +
", " + o);
}
if (oSet.add(o) != true)
throw new IllegalArgumentException(
"duplicate objective:" + o);
}
private List getRecipients(List resList) throws PoolsException
{
List recipientList = new ArrayList();
long size, max;
for (int i = 0; i < resList.size(); i++) {
Resource res;
res = (Resource)resList.get(i);
String type = res.getStringProperty("type");
size = res.getLongProperty(type+".size");
max = res.getLongProperty(type+".max");
if (size < max)
recipientList.add(res);
}
return (recipientList);
}
private List getDonors(List resList) throws PoolsException
{
List donorList = new ArrayList();
long size, min;
for (int i = 0; i < resList.size(); i++) {
Value bValue;
Resource res;
List pinned;
ArrayList valueList = new ArrayList();
res = (Resource)resList.get(i);
String type = res.getStringProperty("type");
size = res.getLongProperty(type+".size");
bValue = new Value("cpu.pinned", true);
valueList.add(bValue);
pinned = res.getComponents(valueList);
bValue.close();
min = res.getLongProperty(type+".min");
if (pinned.size() > min)
size -= pinned.size() - min;
if (size > min)
donorList.add(res);
}
return (donorList);
}
private List getProcessors(Resource set) throws PoolsException
{
Iterator it = set.getComponents(null).iterator();
List ret = new ArrayList();
while (it.hasNext()) {
Component cpu = (Component) it.next();
try
{
if (cpu.getBoolProperty("cpu.pinned") == false)
ret.add(cpu);
} catch (PoolsException pe)
{
ret.add(cpu);
}
}
return (ret);
}
public boolean isValid()
{
return (monitor.isValid());
}
public Monitor getMonitor()
{
return (monitor);
}
public Set getObjectives(Element elem)
{
return ((Set)objMap.get(elem));
}
static class ScoreMove implements Comparable {
private final Move m;
private final double score;
private static final DecimalFormat scoreFormat =
new DecimalFormat("0.00");
public ScoreMove(Move m, double score)
{
this.m = m;
this.score = score;
}
public int compareTo(Object o)
{
ScoreMove other = (ScoreMove) o;
return ((score < other.getScore()) ? -1 :
(score > other.getScore()) ? 1 : 0);
}
public String toString()
{
return ("move (" + m + ") score "
+ scoreFormat.format(score));
}
double getScore()
{
return (score);
}
Move getMove()
{
return (m);
}
}
}