1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * ident "%Z%%M% %I% %E% SMI" 27 */ 28 29 package com.sun.solaris.domain.pools; 30 31 import java.io.*; 32 import java.nio.*; 33 import java.nio.channels.*; 34 import java.util.*; 35 import java.util.regex.*; 36 import java.text.DecimalFormat; 37 38 import com.sun.solaris.service.locality.*; 39 import com.sun.solaris.service.logging.*; 40 import com.sun.solaris.service.pools.*; 41 import com.sun.solaris.service.exception.*; 42 43 /** 44 * The <code>SystemSolver</code> class implements a dynamic resource 45 * allocation solver. The Solver takes a configuration and "solves" 46 * the resource allocation problem. 47 * 48 * This class operates upon a configuration which is suppplied during 49 * initialization. Its responsibilities include: 50 * <ul> 51 * <li><p> 52 * Maintaining decision history 53 * <li><p> 54 * Maintaining information about the locality domain 55 * <li><p> 56 * Maintaining a map of all elements and their associated set of objectives 57 * <li><p> 58 * Identifying objective expressions with a configuration 59 * </ul> 60 */ 61 class SystemSolver implements Solver { 62 /** 63 * The default location of this history file. 64 */ 65 public static final String DH_FILE_DEF_PATH = 66 "/var/adm/pool/history"; 67 68 /** 69 * The property overriding the default decision history path. 70 */ 71 public static final String DH_FILE_PROP_NAME = 72 "system.poold.history-file"; 73 74 /** 75 * The LocalityDomain extracted from the configuration. 76 */ 77 private LocalityDomain ldom; 78 79 /** 80 * The objective map used to link all elements with their set 81 * of objectives. 82 */ 83 private Map objMap; 84 85 /** 86 * The pattern used to identify objective expressions. 87 */ 88 private static final Pattern p0 = Pattern.compile(";"); 89 90 /** 91 * The configuration for which this solver is "solving". 92 */ 93 private Configuration conf; 94 95 /** 96 * Monitor providing statistics for this solver. 97 */ 98 private final Monitor monitor; 99 100 /** 101 * The decision history maintainer. 102 */ 103 private DecisionHistory dh; 104 105 /** 106 * The path to the decision history file. 107 */ 108 private String dhPath; 109 110 /** 111 * The number of CPUs on the system. 112 */ 113 private int cpuCount; 114 115 /** 116 * The number of locality triggered examinations made. 117 */ 118 private int examineCount; 119 120 /** 121 * Constructs a solver which initialises a decision history 122 * maintainer. The decision history is be used during operation 123 * to veto historically-poor decisions output from the objective 124 * function. 125 */ SystemSolver(Monitor monitor)126 SystemSolver(Monitor monitor) 127 { 128 /* 129 * Create a HashMap to store all objectives. 130 */ 131 objMap = new HashMap(); 132 this.monitor = monitor; 133 } 134 135 /** 136 * Initialize the solver for operation upon the supplied 137 * configuration. Possible objective expressions set upon 138 * target elements are identified and stored in an objective 139 * map which associates elements with the set of their 140 * objectives. 141 * 142 * @param conf The configuration to be manipulated. 143 * @throws PoolsException If the initialization fails. 144 */ initialize(Configuration conf)145 public void initialize(Configuration conf) throws PoolsException 146 { 147 String oString = null; 148 String exps[]; 149 150 this.conf = conf; 151 /* 152 * Count the CPUs in the system, this is used to 153 * control locality objective processing in the 154 * examine() method. 155 */ 156 cpuCount = conf.getComponents(null).size(); 157 examineCount = 0; 158 /* 159 * Remove any old objectives 160 */ 161 objMap.clear(); 162 163 /* 164 * Extract any configuration objectives 165 */ 166 try { 167 oString = conf.getStringProperty( 168 "system.poold.objectives"); 169 if (oString.length() > 0) { 170 Set oSet = new HashSet(); 171 objMap.put(conf, oSet); 172 Poold.CONF_LOG.log(Severity.DEBUG, 173 "adding configuration objective " + 174 oString); 175 exps = p0.split(oString); 176 for (int i = 0; i < exps.length; i++) { 177 try { 178 Expression exp = 179 Expression.valueOf( 180 exps[i]); 181 addObjective(oSet, "system", 182 exp); 183 } catch (IllegalArgumentException iae) { 184 Poold.utility.warn( 185 Poold.CONF_LOG, iae, false); 186 } 187 } 188 } 189 } catch (PoolsException pe) { 190 /* 191 * Ignore as this means there is no objective 192 * property 193 */ 194 } 195 /* 196 * Now extract all pset objectives 197 */ 198 Value typeProp = new Value("type", "pset"); 199 List valueList = new LinkedList(); 200 valueList.add(typeProp); 201 Iterator resIt = conf.getResources(valueList).iterator(); 202 typeProp.close(); 203 while (resIt.hasNext()) { 204 Resource resource = (Resource)resIt.next(); 205 206 try { 207 oString = resource.getStringProperty( 208 "pset.poold.objectives"); 209 if (oString.length() > 0) { 210 Set oSet = new HashSet(); 211 objMap.put(resource, oSet); 212 Poold.CONF_LOG.log(Severity.DEBUG, 213 "adding " + 214 resource.getStringProperty( 215 "pset.name") + 216 " objective \"" + oString + "\""); 217 exps = p0.split(oString); 218 for (int i = 0; i < exps.length; i++) { 219 Expression exp = null; 220 try { 221 exp = Expression. 222 valueOf(exps[i]); 223 addObjective(oSet, 224 "pset", exp); 225 } catch 226 (IllegalArgumentException e) 227 { 228 Poold.utility.warn( 229 Poold.CONF_LOG, e, 230 false); 231 } 232 } 233 } 234 } catch (PoolsException pe) { 235 continue; 236 } 237 } 238 Poold.CONF_LOG.log(Severity.DEBUG, "objective map: " + 239 objMap.toString()); 240 241 /* 242 * Capture the LocalityDomain details. 243 */ 244 if (ldom != null) { 245 ldom.close(); 246 } 247 try { 248 ldom = new LocalityDomain(LocalityDomain.LGRP_VIEW_OS); 249 } catch (Exception e) { 250 Poold.utility.die(Poold.OPT_LOG, e); 251 } 252 253 /* 254 * Load or create the decision history. 255 */ 256 String newDhPath; 257 try { 258 newDhPath = conf.getStringProperty( 259 DH_FILE_PROP_NAME); 260 } catch (PoolsException pe) { 261 newDhPath = DH_FILE_DEF_PATH; 262 } 263 264 if (!newDhPath.equals(dhPath)) { 265 try { 266 dh = DecisionHistory.loadFromFile(newDhPath); 267 Poold.CONF_LOG.log(Severity.DEBUG, 268 "loaded history file " + newDhPath); 269 } catch (Exception e) { 270 if (!(e instanceof FileNotFoundException)) { 271 Poold.CONF_LOG.log(Severity.WARNING, 272 newDhPath + 273 ": contents unusable; ignoring"); 274 Poold.CONF_LOG.log(Severity.DEBUG, 275 newDhPath + ": contents unusable", 276 e); 277 } 278 /* 279 * Use current DecisionHistory instead, 280 * if any. 281 */ 282 if (dh == null) 283 dh = new DecisionHistory(); 284 } 285 286 /* 287 * Try using the new path. 288 */ 289 try { 290 dh.syncToFile(newDhPath); 291 } catch (Exception e) { 292 Poold.utility.die(Poold.CONF_LOG, 293 new PooldException(newDhPath + 294 ": couldn't synchronize history file") 295 .initCause(e), false); 296 } 297 dhPath = newDhPath; 298 } 299 } 300 301 /** 302 * Determine if the given resource has non-workload-dependent 303 * objectives. 304 * @param elem The element to examine. 305 */ hasNonWorkloadDependentObjectives(Element elem)306 private boolean hasNonWorkloadDependentObjectives(Element elem) 307 { 308 Set elemObj = (Set)objMap.get(elem); 309 310 /* 311 * This code relies upon the fact that an element with 312 * no objectives will not have an empty set, but rather 313 * no value in the objMap. 314 */ 315 316 if (elemObj == null) 317 return (false); 318 319 Iterator elemObjIt = elemObj.iterator(); 320 while (elemObjIt.hasNext()) 321 if (elemObjIt.next() instanceof 322 WorkloadDependentObjective) 323 return (false); 324 325 return (true); 326 } 327 328 /** 329 * Determine if the given resource has workload-dependent 330 * objectives. 331 * @param elem The element to examine. 332 */ hasWorkloadDependentObjectives(Element elem)333 private boolean hasWorkloadDependentObjectives(Element elem) 334 { 335 Set elemObj = (Set)objMap.get(elem); 336 337 /* 338 * This code relies upon the fact that an element with 339 * no objectives will not have an empty set, but rather 340 * no value in the objMap. 341 */ 342 343 if (elemObj == null) 344 return (false); 345 346 Iterator elemObjIt = elemObj.iterator(); 347 while (elemObjIt.hasNext()) 348 if (elemObjIt.next() instanceof 349 WorkloadDependentObjective) 350 return (true); 351 352 return (false); 353 } 354 355 /** 356 * Return true if the monitored configuration should be 357 * reconfigured. All workload-dependent objectives are 358 * examined to determine if the configuration is still 359 * satisfying all specified objectives on all elements. If any 360 * objectives are failing, then this method will return true. 361 * 362 * @param mon The monitoring object used to assess objective 363 * compliance. 364 */ examine(Monitor mon)365 public boolean examine(Monitor mon) throws PoolsException, 366 StaleMonitorException 367 { 368 /* 369 * Take advantage of the guaranteed-valid monitor data 370 * for measuring the improvement of any past decisions. 371 */ 372 dh.expireAndMeasureImprovements(mon); 373 374 Iterator objIt = objMap.keySet().iterator(); 375 boolean ret = false; 376 boolean hasLocalityObjectives = false; 377 boolean isMonitorValid = true; 378 379 /* 380 * All objectives are examined, even though failure 381 * could be detected earlier. This is because logging 382 * of all objectives is required and the information 383 * about failure can be stored and used during 384 * solving. 385 */ 386 while (objIt.hasNext()) { 387 Element elem = (Element) objIt.next(); 388 Set elemObj = (Set) objMap.get(elem); 389 Iterator elemObjIt = elemObj.iterator(); 390 while (elemObjIt.hasNext()) { 391 Objective obj = (Objective) elemObjIt.next(); 392 Poold.OPT_LOG.log(Severity.DEBUG, 393 "checking objective " + obj); 394 if (obj instanceof WorkloadDependentObjective) { 395 if (isValid()) { 396 /* 397 * If any objectives 398 * are violated, then 399 * we must 400 * reconfigure, so 401 * check them all in 402 * turn. 403 */ 404 ret = ((WorkloadDependentObjective) 405 obj).examine(conf, this, elem) || 406 ret; 407 } else 408 isMonitorValid = false; 409 } 410 } 411 412 /* 413 * Check if this is the first element, seen in 414 * this pass, that has locality objectives. 415 */ 416 if (!hasLocalityObjectives && 417 hasNonWorkloadDependentObjectives(elem)) 418 hasLocalityObjectives = true; 419 } 420 if (isMonitorValid == false) { 421 Poold.MON_LOG.log(Severity.INFO, 422 "not evaluating workload-dependent objectives " + 423 "until sufficient statistics are collected"); 424 } 425 /* 426 * If we don't have locality objectives, we don't force 427 * the reexamination. This is controlled by 428 * hasLocalityObjectives. 429 * 430 * So that we don't continually trigger 431 * reconfiguration examinations for locality 432 * objectives we stop forcing recalculations when we 433 * reach cpuCount / 2. This should be enough moves to 434 * get good locality for those configurations which 435 * have no WorkloadDependentObjectives. 436 */ 437 return (ret || (hasLocalityObjectives && (examineCount++ < 438 cpuCount / 2))); 439 } 440 441 /** 442 * Reallocate resources in a configuration to achieve user 443 * specified objectives. Return true if the configuration has 444 * been updated, false otherwise. 445 * 446 * This method should only be invoked if a previous 447 * examination of the configuration which is monitored 448 * indicates that objectives are failing. The monitored 449 * configuration is re-opened for editing, locking that 450 * configuration for the duration of this operation. 451 * 452 * @throws Exception If the solve fails. 453 */ solve()454 public boolean solve() throws Exception 455 { 456 boolean madeMove = false; 457 /* 458 * All solving operations must be done in an 459 * "editable" context, so create a new modifiable 460 * configuration which is at the same location as the 461 * monitored configuration 462 */ 463 Configuration rwConf = new Configuration(conf.getLocation(), 464 PoolInternal.PO_RDWR); 465 466 try { 467 /* 468 * Build a resource set importance map for use 469 * when propagating pool importance to each 470 * possible solution. Use the same logic as 471 * libpool and let a resource take the highest 472 * importance value from all pools associated 473 * with the set. 474 */ 475 Map resImp = new HashMap(); 476 List poolList = rwConf.getPools(null); 477 Iterator itPool = poolList.iterator(); 478 while (itPool.hasNext()) { 479 Pool pool = (Pool) itPool.next(); 480 long newImp = pool. 481 getLongProperty("pool.importance"); 482 List resList = pool.getResources(null); 483 Iterator itRes = resList.iterator(); 484 while (itRes.hasNext()) { 485 Resource res = (Resource) itRes.next(); 486 if (resImp.containsKey(res)) { 487 Long imp = (Long) resImp. 488 get(res); 489 if (newImp > imp.longValue()) 490 resImp.put(res, 491 Long.valueOf( 492 newImp)); 493 } else 494 resImp.put(res, 495 Long.valueOf(newImp)); 496 } 497 } 498 /* 499 * Consider all possible alternative 500 * configurations. This list is generated as a 501 * series of moves. Administrative constraints 502 * are applied to the moves to prune the list of 503 * possible configurations. 504 */ 505 Value val = new Value("type", "pset"); 506 List valueList = new LinkedList(); 507 valueList.add(val); 508 509 510 List resList = rwConf.getResources(valueList); 511 val.close(); 512 List donors = getDonors(resList); 513 List receivers = getRecipients(resList); 514 Poold.OPT_LOG.log(Severity.DEBUG, "donors: " + 515 donors); 516 Poold.OPT_LOG.log(Severity.DEBUG, "receivers: " + 517 receivers); 518 Iterator itDonor = donors.iterator(); 519 List moves = new ArrayList(); 520 while (itDonor.hasNext()) { 521 Resource donor = (Resource) itDonor.next(); 522 List processors = getProcessors(donor); 523 Poold.OPT_LOG.log(Severity.DEBUG, 524 "donor processors: " + processors); 525 Iterator itProcessor = processors.iterator(); 526 while (itProcessor.hasNext()) { 527 Component cpu = (Component) itProcessor. 528 next(); 529 Iterator itReceiver = receivers. 530 iterator(); 531 while (itReceiver.hasNext()) { 532 Resource receiver = 533 (Resource) itReceiver. 534 next(); 535 /* 536 * Can't move to yourself 537 */ 538 if (receiver == donor) 539 continue; 540 moves.add(new ComponentMove( 541 donor, receiver, cpu)); 542 } 543 } 544 } 545 Poold.OPT_LOG.log(Severity.DEBUG, 546 "potential moves: " + moves); 547 /* 548 * Now that we have our alternative configurations, 549 * score each configuration by applying all objectives 550 * to each configuration. Hold the scores in the 551 * score set. 552 */ 553 HashSet scores = new HashSet(); 554 Iterator itMoves = moves.iterator(); 555 while (itMoves.hasNext()) { 556 double totalContrib = 0; 557 Move move = (Move) itMoves.next(); 558 Iterator objIt = objMap.keySet().iterator(); 559 while (objIt.hasNext()) { 560 Element elem = (Element) objIt.next(); 561 Set elemObj = (Set) objMap.get(elem); 562 Iterator elemObjIt = elemObj.iterator(); 563 while (elemObjIt.hasNext()) { 564 Objective obj = 565 (Objective)elemObjIt.next(); 566 if (obj instanceof 567 LocalityObjective) 568 ((LocalityObjective)obj) 569 .prepare(ldom, 570 (Resource)elem); 571 /* 572 * If the monitor is 573 * invalid, do not 574 * process 575 * WorkloadDependentObjectives 576 * since they have an 577 * implicit dependency 578 * on the monitor 579 * data. 580 */ 581 if (obj instanceof 582 WorkloadDependentObjective) 583 if (!isValid()) 584 continue; 585 double contrib = obj.calculate( 586 rwConf, move, elem); 587 if (contrib < -1 || contrib > 1) 588 throw new 589 IllegalOFValueException( 590 "x: " + contrib + 591 " is invalid, legal " + 592 "range is -1 <= x <= " + 593 "1"); 594 /* 595 * Modify the basic 596 * score by the 597 * importance of the 598 * objective and (if 599 * appropriate) the 600 * importance of an 601 * associated pool. 602 */ 603 if (resImp.containsKey(elem)) { 604 contrib *= ((Long) 605 resImp.get(elem)). 606 longValue(); 607 } 608 609 totalContrib += contrib * 610 obj.getExpression(). 611 getImportance(); 612 } 613 } 614 Poold.OPT_LOG.log(Severity.DEBUG, 615 "scored move (" + move + ") " + 616 totalContrib); 617 scores.add(new ScoreMove(move, totalContrib)); 618 } 619 if (scores.size() != 0) { 620 /* 621 * Try to find a move to apply which 622 * yields a positive contribution. 623 */ 624 Object scoresArray[] = scores.toArray(); 625 Arrays.sort(scoresArray, 626 Collections.reverseOrder()); 627 if ((madeMove = processMoves(rwConf, 628 scoresArray, false)) == false) 629 madeMove = processMoves(rwConf, 630 scoresArray, true); 631 } else 632 Poold.OPT_LOG.log(Severity.INFO, 633 "no moves found"); 634 rwConf.close(); 635 Poold.OPT_LOG.log(Severity.DEBUG, 636 "synchronizing decision history"); 637 dh.syncToFile(dhPath); 638 } catch (Exception ex) { 639 rwConf.close(); 640 throw ex; 641 } 642 return (madeMove); 643 } 644 645 /* 646 * Process the supplied array of scored moves, trying to find 647 * a move to apply. Return true if a move could be applied, 648 * false otherwise. 649 * 650 * @param conf The configuration to be modified. 651 * @param scores The areay of scored moves to be tried. 652 * @param ignoreDH Ignore Decision History details. 653 */ processMoves(Configuration rwConf, Object scores[], boolean ignoreDH)654 private boolean processMoves(Configuration rwConf, Object scores[], 655 boolean ignoreDH) throws PoolsException, StaleMonitorException 656 { 657 boolean madeMove = false; 658 659 for (int i = 0; i < scores.length; i++) { 660 ScoreMove move = (ScoreMove) scores[i]; 661 if (move.getScore() <= 0) { 662 if (ignoreDH) 663 Poold.OPT_LOG.log(Severity.INFO, 664 move + " not applied as " + 665 "benefit not significant"); 666 break; 667 } 668 if ((madeMove = applyMove(rwConf, move, ignoreDH)) == 669 true) 670 break; 671 } 672 return (madeMove); 673 } 674 675 /* 676 * Attempt to apply the supplied move to the 677 * configuration. Return true if the move could be applied, 678 * false otherwise. 679 * 680 * @param conf The configuration to be modified. 681 * @param move The move to be applied. 682 * @param ignoreDH Ignore Decision History details. 683 */ applyMove(Configuration conf, ScoreMove move, boolean ignoreDH)684 private boolean applyMove(Configuration conf, ScoreMove move, 685 boolean ignoreDH) 686 throws PoolsException, StaleMonitorException 687 { 688 boolean madeMove = false; 689 boolean wdpInvolved = false; 690 double utilization = 0.0; 691 692 Poold.OPT_LOG.log(Severity.DEBUG, "selected " + move); 693 if (hasWorkloadDependentObjectives(move.getMove().getTo()) || 694 hasWorkloadDependentObjectives(conf)) { 695 Poold.OPT_LOG.log(Severity.DEBUG, 696 "Attempting to retrieve utilization for:" + move 697 .getMove().getTo()); 698 utilization = monitor.getUtilization(move 699 .getMove().getTo()); 700 wdpInvolved = true; 701 } 702 703 /* 704 * Unless a move can be vetoed (i.e. decision history 705 * is effective and there are is a workload-dependent 706 * involved), the move should alwways be applied. 707 */ 708 if (ignoreDH || !wdpInvolved || !dh.veto(move.getMove(), 709 utilization)) { 710 Poold.OPT_LOG.log(Severity.INFO, 711 "applying move " + move.getMove()); 712 move.getMove().apply(); 713 ResourceMonitor mon = getMonitor().get(move.getMove(). 714 getFrom()); 715 mon.resetData("utilization"); 716 mon = getMonitor().get(move.getMove().getTo()); 717 mon.resetData("utilization"); 718 try { 719 Poold.OPT_LOG.log(Severity.DEBUG, 720 "committing configuration"); 721 conf.commit(0); 722 try { 723 if (move.getMove() instanceof 724 ComponentMove) 725 if (wdpInvolved) 726 dh.recordProcessorMove( 727 (ComponentMove)move 728 .getMove(), 729 utilization, 730 monitor 731 .getSampleCount()); 732 else 733 Poold.OPT_LOG.log( 734 Severity.DEBUG, 735 "decision not " + 736 "recorded due to " + 737 "lack of workload-" 738 + "dependent " + 739 "objectives"); 740 } catch (Exception e) { 741 Poold.OPT_LOG.log(Severity.INFO, 742 "couldn't update " + 743 "decision history (" + 744 e.toString() + ")"); 745 } 746 madeMove = true; 747 } catch (PoolsException pe) { 748 conf.rollback(); 749 Poold.OPT_LOG.log(Severity.INFO, 750 "move failed, possibly due to a " + 751 "bound process in a 1-processor " + 752 "set"); 753 } 754 } else { 755 /* 756 * Move was vetoed. 757 */ 758 if (!ignoreDH && wdpInvolved) 759 Poold.OPT_LOG.log(Severity.INFO, 760 move.getMove() + " not applied due to " + 761 "poor past results"); 762 } 763 return (madeMove); 764 } 765 766 /** 767 * Add an objective based on the supplied expression to the 768 * supplied set of objectives. 769 * 770 * @param oSet Set of objectives to be extended 771 * @param type Type of element to which the expression is applied 772 * @param exp Expression to be used in the objective 773 * @throws IllegalArgumentException If a duplicate objective 774 * is identified or an invalid expression is supplied for this 775 * type of element 776 */ addObjective(Set oSet, String type, Expression exp)777 private void addObjective(Set oSet, String type, Expression exp) 778 throws IllegalArgumentException 779 { 780 Objective o = AbstractObjective.getInstance(type, exp); 781 Poold.CONF_LOG.log(Severity.DEBUG, "parsed objective " + o); 782 /* 783 * Check the set of objectives and find contradictions. 784 */ 785 Iterator itObjs = oSet.iterator(); 786 while (itObjs.hasNext()) { 787 Objective other = (Objective) itObjs.next(); 788 if (o.getExpression().contradicts( 789 other.getExpression())) 790 throw new IllegalArgumentException( 791 "contradictory objectives:" + other + 792 ", " + o); 793 } 794 795 if (oSet.add(o) != true) 796 throw new IllegalArgumentException( 797 "duplicate objective:" + o); 798 } 799 800 /** 801 * Return a list of resource sets prepared to receive 802 * resources 803 * 804 * The list consists of all resource setss (of the supplied 805 * type) whose size is < their max constraint. 806 * 807 * @param resList The list of all resource sets from which 808 * recipients will be chosen 809 * @throws PoolsException if there is an error manipulation 810 * the pool resources 811 */ getRecipients(List resList)812 private List getRecipients(List resList) throws PoolsException 813 { 814 List recipientList = new ArrayList(); 815 long size, max; 816 for (int i = 0; i < resList.size(); i++) { 817 Resource res; 818 res = (Resource)resList.get(i); 819 String type = res.getStringProperty("type"); 820 size = res.getLongProperty(type+".size"); 821 max = res.getLongProperty(type+".max"); 822 if (size < max) 823 recipientList.add(res); 824 } 825 return (recipientList); 826 } 827 828 /** 829 * Return a list of resource sets prepared to donate resources 830 * 831 * The list consists of all resource sets (of the supplied 832 * type) whose size (minus the number of pinned resources 833 * where applicable) is > their min constraint. 834 * 835 * @param resList The list of all resource sets from which 836 * recipients will be chosen 837 * @throws PoolsException if there is an error manipulation 838 * the pool resources 839 */ getDonors(List resList)840 private List getDonors(List resList) throws PoolsException 841 { 842 List donorList = new ArrayList(); 843 long size, min; 844 for (int i = 0; i < resList.size(); i++) { 845 Value bValue; 846 Resource res; 847 List pinned; 848 ArrayList valueList = new ArrayList(); 849 850 res = (Resource)resList.get(i); 851 String type = res.getStringProperty("type"); 852 size = res.getLongProperty(type+".size"); 853 bValue = new Value("cpu.pinned", true); 854 valueList.add(bValue); 855 pinned = res.getComponents(valueList); 856 bValue.close(); 857 min = res.getLongProperty(type+".min"); 858 if (pinned.size() > min) 859 size -= pinned.size() - min; 860 if (size > min) 861 donorList.add(res); 862 } 863 return (donorList); 864 } 865 866 /** 867 * Return a list of Processors for the supplied resource. 868 * 869 * The list consists of all Processors (excluding the pinned 870 * Processors) in the set. 871 * 872 * @param set The resource for which Processors should be found 873 * @throws PoolsException if there is an error manipulation 874 * the pool resources 875 */ getProcessors(Resource set)876 private List getProcessors(Resource set) throws PoolsException 877 { 878 Iterator it = set.getComponents(null).iterator(); 879 List ret = new ArrayList(); 880 881 while (it.hasNext()) { 882 Component cpu = (Component) it.next(); 883 try 884 { 885 if (cpu.getBoolProperty("cpu.pinned") == false) 886 ret.add(cpu); 887 } catch (PoolsException pe) 888 { 889 ret.add(cpu); 890 } 891 } 892 return (ret); 893 } 894 895 /** 896 * Return true if the solver is capable of working with 897 * statistically valid data. 898 */ isValid()899 public boolean isValid() 900 { 901 return (monitor.isValid()); 902 } 903 904 /** 905 * Return the monitor used by this solver. 906 */ getMonitor()907 public Monitor getMonitor() 908 { 909 return (monitor); 910 } 911 912 /** 913 * Return the set of objectives associated with the supplied 914 * element. 915 * 916 * @param elem Retrieve objectives for this element. 917 */ getObjectives(Element elem)918 public Set getObjectives(Element elem) 919 { 920 return ((Set)objMap.get(elem)); 921 } 922 923 /** 924 * Holds details about the score of a proposed configuration 925 * move. Each move must be scored so that they can ranked in 926 * terms of increasing desirability 927 */ 928 static class ScoreMove implements Comparable { 929 /** 930 * The move which is being scored. 931 */ 932 private final Move m; 933 934 /** 935 * The score of the move. 936 */ 937 private final double score; 938 939 /** 940 * Score formatter. 941 */ 942 private static final DecimalFormat scoreFormat = 943 new DecimalFormat("0.00"); 944 945 /** 946 * Constructor. 947 * 948 * @param m The move under consideration. 949 * @param score The score of the move. 950 */ ScoreMove(Move m, double score)951 public ScoreMove(Move m, double score) 952 { 953 this.m = m; 954 this.score = score; 955 } 956 compareTo(Object o)957 public int compareTo(Object o) 958 { 959 ScoreMove other = (ScoreMove) o; 960 961 return ((score < other.getScore()) ? -1 : 962 (score > other.getScore()) ? 1 : 0); 963 } 964 toString()965 public String toString() 966 { 967 return ("move (" + m + ") score " 968 + scoreFormat.format(score)); 969 } 970 971 /** 972 * Return the score. 973 */ getScore()974 double getScore() 975 { 976 return (score); 977 } 978 979 /** 980 * Return the move. 981 */ getMove()982 Move getMove() 983 { 984 return (m); 985 } 986 } 987 } 988