/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* ident "%Z%%M% %I% %E% SMI"
*/
/*
* poold overview
* ----- --------
*
* poold manipulates system resources in accordance with administrator
* specified constraints and objectives. The "goal" of the application
* is to maximise the efficiency of available resources within these
* parameters.
*
* Constraints are specified as follows:
*
* On a resource set:
*
* - min Is the minimum amount of resource a set should
* receive. poold will never elect to move resource so that a set
* falls below its minimum value. It is possible for a set to
* fall below its minimum as a consequence of administrative
* intervention, in which case poold will endeavour to bring a
* set back to its minimum level at the earliest opportunity.
*
* - max Is the maximum amount of resource a set should
* recieve. poold will never elect to move resource so that a set
* rises above its maximum value. It is possible for a set to
* rise above its maximum as a consequence of administrative
* intervention, in which case poold will endeavour to bring a
* set back to its maximum level at the earliest opportunity.
*
* On a resource component:
*
* - cpu.pinned Is an indication that a CPU should be ignored by
* poold for purposes of reallocation. A pinned CPU will never be
* moved by poold from one set to another.
*
* In addition to constraints, an administrator may also specify
* objectives. Currently three types of objectives are supported:
*
* - system.wt-load Is an objective set across an entire
* configuration. It attempts to ensure that resource is shared
* in accordance with current consumption. Those partitions which
* are most heavily utilized are give more resource in an attempt
* to lower their utilization levels.
*
* - set.locality Is a locality objective which attempts to
* minimize or maximize resource locality for a set.
*
* - set.utilization Is a utilization based objective which an
* administrator may use to explicitly dictate the utilization
* levels which should be achieved for a set.
*
* When executing, poold uses information about the current pools
* configuration; monitored resource utilization and specified
* constraints and objectives to determine if a move of resources is
* likely to lead to an increase in objective satisfaction.
*
* Candidate moves are generated by observing resource constraints.
* These moves are evaluated and scored in terms of their contribution
* to objective satisfaction (note: objectives are weighted according
* to their importance) and ranked accordingly. If a move can be
* identified that delivers an improvement, the move is made. Data is
* collected about past moves and recorded as "Decision History",
* before the move is made this data is consulted and if the move is
* expected not to yield an improvement, it may be cancelled. This
* refinement is designed to improve the quality of decision making by
* reflecting upon the past performance of decisions as well as the
* contribution to objective satisfaction.
*
* poold structure
* ----- ---------
*
* The common package name begins with:
*
* com.sun.solaris
*
* The software is divided into two main functional areas:
*
* service
*
* These packages collaborate to provide services to poold
* (typically, they use JNI to access exising
* functionality). They are not designed to be extended. For more
* details on these classes examine the source files in each
* directory.
*
* exception Stack trace formatter
* kstat Interface to Solaris kstat facility
* locality Interface to Solaris lgrp facility
* logging Interface to Solaris syslog facility
* pools Interface to Solaris libpool facility
* timer High resolution timestamps
*
* domain:
*
* These package reflect problem domain concepts and are
* responsible for application logic related to these
* concepts.
*
* pools Dynamic Resource Pools specific functionality.
*
* This code block will continue to explain in more detail how poold
* is organized.
*
* poold provides the following basic facilities:
*
* Monitoring:
*
* Basic statistic access is provided by the
* com.sun.solaris.service.kstat package. The following interfaces and
* classes collaborate to provide higher level statistic and
* monitoring facilities to the application:
*
* INTERFACES
*
* AggregateStatistic
* Monitor
* Statistic
* StatisticListener
*
* CLASSES
*
* AbstractStatistic
* DoubleStatistic
* LongStatistic
* ResourceMonitor
* StatisticEvent
* StatisticList
* StatisticOperations
* SystemMonitor
* UnsignedInt64Statistic
*
* Logging:
*
* Logging services are provided by the com.sun.solaris.logging
* package. In addition, the following class implements Poold's
* specific logging requirements.
*
* CLASSES
*
* Poold
*
* Optimization:
*
* lgrp service are provided by the com.sun.solaris.service.lgrp
* package. pools services are provided by the
* com.sun.solaris.service.pools package. In addition, optimization is
* implemented in the following Interfaces and Classes:
*
* INTERFACES
*
* Objective
* Solver
* WorkloadDependentObjective
*
* CLASSES
*
* AbstractObjective
* ComponentMove
* DecisionHistory
* Expression
* KExpression
* KVEpression
* KVOpExpression
* LocalityObjective
* Move
* Poold
* QuantityMove
* SystemSolver
* UtilizationObjective
* WeightedLoadObjective
*
* Configuration:
*
* pools services are provided by the com.sun.solaris.service.pools
* package, this is used to read poold configuration details from a
* libpool configuration. In addition, configuration is implemented in
* the following Classes:
*
* CLASSES
*
* AbstractObjective
* Expression
* Poold
* SystemSolver
*
* (NB: Some classes were mentioned in multiple categories where there
* responsbilities overlap. Inner classes are not listed as their
* responsibilities can be clearly inferred from their context.)
*
* For more details on any of the packages, classes or interfaces
* mentioned above, look at the documentation associated with each
* class.
*/
/**
* The <code>Poold</code> class implements a dynamic resource
* allocation system for Solaris.
*
* <code>Poold</code> is a monitoring daemon, designed to evaluate
* user specified objectives, monitor workload behaviour and
* dynamically assign resources in order to satisfy the evaluated
* objectives. For more details see:
*
* <a href="http://sac.eng.sun.com/PSARC/2002/287/">PSARC/2002/287</a>
*/
final class Poold
{
/**
* The configuration which is manipulated.
*/
/**
* The monitoring interface.
*/
/**
* The interface to higher level resource managers.
*/
/**
* The interface to the configuration solver.
*/
/**
* Default path to the logging properties file.
*/
"/usr/lib/pool/poold.properties";
/**
* Logger for records which aren't produced in the Monitoring,
* Configuration, or Optimization states. This logger is the
* parent to the loggers used in those states.
*/
"com.sun.solaris.domain.pools.poold");
/**
* Logger for records produced in the Configuration state.
*/
"com.sun.solaris.domain.pools.poold.Configuration");
/**
* Logger for records produced in the Monitoring state.
*/
"com.sun.solaris.domain.pools.poold.Monitoring");
/**
* Logger for records produced in the Optimization state.
*/
"com.sun.solaris.domain.pools.poold.Optimization");
/**
* Singleton instance of Poold.
*/
/**
* The main sampling and solving thread.
*/
/**
* Process exit code indicating a failure.
*/
/**
* Keep track of whether initialize() has been invoked, to
* output the "starting" message on the first.
*/
/**
* Flags whether poold should run or exit.
*/
private static class logHelper {
/**
* Default logfile location
*/
/**
* Log location indicating <code>syslog</code>, as
* opposed to a file, should be used.
*/
/**
* Default Log severity (if not overridden)
*/
/**
* Name of configuration property, log location.
*/
"system.poold.log-location";
/**
* Name of configuration property, log level.
*/
"system.poold.log-level";
/**
* Location of logfile -- an absolute filename, or
* "SYSLOG".
*/
/**
* Logfile handler, responsible for taking log messages
* and exporting them.
*/
/**
* Logfile severity, log messages below this severity are
* ignored.
*/
/**
* Flag recording whether preinitialization has occurred.
*/
private static boolean preinitialized = false;
/**
* Flag recording whether the logging Severity has been
* overridden with the -l command-line option, which
* means the console is the only thing being logged to,
* and the configuration's logging properties are
* ignored.
*/
private static boolean usingConsole;
/**
* Indicates whether logging semantics should be changed
* to facilitate debugging.
*/
private static final boolean loggingDebugging = false;
/**
* Do the pre-initialization initialization: install
* loggers for reporting errors during initialization.
*
* @param consoleSeverity If non-null, indicates that
* the configuration property-controlled logging behavior
* is to be overridden (the <code>-l</code> option was
* specified), and messages are to be logged only to the
* console, with (at most) the given maximum severity.
*/
if (preinitialized)
return;
/*
* Read logging properties affecting the
* FileHandler and ConsoleHandler from
* <code>poold.properties</code>.
*/
try {
new ByteArrayInputStream(
bos.toByteArray()));
} catch (IOException ioe) {
+ "read logging properties from "
+ "poold.properties: " + ioe);
}
/*
* the default location, until the pools
* configuration properties are read and
* applied, which may change the logging
* file and severity.
*
* Under normal circumstances, it's
* expected that NO INFO-level messages
* will be emitted until that time; this
* is only a measure to ensure that
* unanticipated errors are reported in
* some log.
*/
}
if (consoleSeverity != null) {
/*
* If -l is specified, log to the
* console. Unless loggingDebug is
* true, this will also mean that the
* logging properties are ignored.
*
* Determine if the user has specified
* the use of a ConsoleHandler through
*/
null; i++)
if (handler[i]
instanceof ConsoleHandler)
/*
* If none was previously, install a
* ConsoleHandler.
*/
ch = new ConsoleHandler();
new SysloglikeFormatter());
}
"logging with level " + severity);
/**
* Allow logging properties to be
* effective if loggingDebugging is not
* set.
*/
if (!loggingDebugging)
usingConsole = true;
}
preinitialized = true;
}
/**
* Configure loggers based on the logging-related
* configuration properties. Outputs a description of
* any changes to the configuration logger.
*
* @throws ConfigurationException if there is an error
* applying libpool configuration properties to
* <code>poold</code>
*/
public static void initializeWithConfiguration(
{
/*
* Set the log location as specified by the
* configuration's system properties.
*/
try {
} catch (PoolsException e) {
}
try {
assert(newLogSeverity != null);
} catch (PoolsException e) {
} catch (IllegalArgumentException e) {
throw(ConfigurationException)
(new ConfigurationException(
"invalid " + PROPERTY_NAME_LOG_LEVEL +
"value: " + newLogSeverityName)
.initCause(e));
}
/*
* (Re)install the logger for the poold class
* hierarchy. This means that only poold
* messages are controlled by the pools
* configuration properties/command-line
* options.
*/
/*
* The logfile is always re-opened, in case the
* cause for reinitialization is due to SIGHUP
* following a log rotation.
*/
}
else {
throw
" value is not an" +
" absolute path");
try {
new FileHandler(
new SysloglikeFormatter());
new PooldException(
": can't write")
}
}
"logging with level " + severity);
}
/**
* Configure the loggers based on the pool's logging
* properties, or, if the -l option was specified on the
* command line, continue to use the console.
*/
throws ConfigurationException
{
if (usingConsole)
return;
else
}
/**
* Return the current logging level.
*/
{
return (severity);
}
public static void close()
{
}
}
}
/**
* Constructor
*
* Only one poold instance should be running per system.
*
* @param consoleSeverity If non-null, indicates that the
* configuration property-controlled logging behavior is to be
* overridden (the <code>-l</code> option was specified), and
* messages are to be logged only to the console, with (at most)
* the given maximum severity.
*/
{
/*
* Establish loggers for recording errors during
* initialization. Under normal circumstances, no
* messages will be emitted; this is only a measure to
* make sure that unanticipated errors are reported in
* some log, or the console, if the -l option is used.
*/
/*
* Try opening the configuration read-write in hopes the
* ability will be possessed henceforth.
*/
try {
} catch (PoolsException pe) {
"cannot open dynamic pools configuration " +
}
try {
} catch (PoolsException pe) {
}
/*
* Create the required sub-components:
* - a monitoring object
* - a DRM implementer
* - a solver
*/
monitor = new SystemMonitor();
}
/**
* Returns a reference to the singleton <code>Poold</code>,
* constructing one if necessary.
*
* @param consoleSeverity If non-null, indicates that the
* configuration property-controlled logging behavior is to be
* overridden (the <code>-l</code> option was specified), and
* messages are to be logged only to the console, with (at most)
* the given maximum severity.
* @throws IllegalArgumentException if the given console
* severity doesn't match that of an existing instance.
*/
{
else
if (logHelper.usingConsole == false &&
return (instance);
else
throw new IllegalArgumentException();
}
/**
* Initializes <code>Poold</code> for operation at startup or
* in response to a detected libpool configuration change.
*/
private void initialize()
{
try {
if (firstInitialization.get())
/*
* When a system is extremely busy, it may
* prove difficult to initialize poold. Just
* keep trying until we succeed.
*/
boolean busy = true;
busy = false;
try {
"configuring solver...");
"configuration complete");
} catch (PoolsException pe) {
"The system is too busy to " +
"initialize, attempting " +
"initialization again");
/*
* pause for a while before
* re-attempting the
* initialization.
*/
try {
} catch (InterruptedException ie) {
/*
* Safe to ignore this
* exception as we
* will simply try
* again sooner.
*/
}
busy = true;
} catch (StaleMonitorException sme) {
"The system is too busy to " +
"initialize, attempting " +
"initialization again");
/*
* pause for a while before
* re-attempting the
* initialization.
*/
try {
} catch (InterruptedException ie) {
/*
* Safe to ignore this
* exception as we
* will simply try
* again sooner.
*/
}
busy = true;
}
}
if (firstInitialization.get())
firstInitialization.set(false);
} catch (ConfigurationException ce) {
}
}
/**
* Execute <code>Poold</code> indefinitely. This method is
* invoked after <code>Poold</code> completes
* configuration. It will continue to execute until
* <code>Poold</code> is terminated.
*
* @throws Exception If an there is an error in execution.
*/
{
int changed = 0;
boolean confRequired = false;
try {
assert(!confRequired || confRequired &&
changed != 0);
if (changed != 0) {
"configuration change detected");
if (!confRequired)
"configuration changed " +
"externally");
"reconfiguring...");
}
confRequired = false;
} catch (PoolsException pe) {
}
if (changed != 0)
initialize();
boolean gotNext = false;
try {
gotNext = true;
/*
* All workload-dependent
* objectives must now be
* checked for violations. The
* solver holds all objectives
* and it makes the decision
* about whether a
* reconfiguration is required.
*/
"reconfiguration required");
} else {
"all evaluated objectives"
+ " satisfied");
}
} catch (StaleMonitorException e) {
/*
* Later, assert that every
* cause of the
* StaleMonitorException is
* handled by the above
* conf.update().
*/
confRequired = true;
} catch (InterruptedException ie) {
"interrupted");
break;
}
}
break;
}
}
/**
* Cleanup any resources when the application terminates.
*/
private void cleanup()
{
}
/**
* Invoke <code>Poold</code>. This main function is provided
* so that <code>poold</code> can be executed. Execution will
* continue indefinitely unless there is an error, in which case
* when execute() terminates.
*/
public void run()
{
public void run() {
try {
cleanup();
} catch (Throwable t) {
}
}
});
try {
initialize();
execute();
} catch (Throwable t) {
}
}
/**
* Stops <code>Poold</code>. Sets a flag indicating that run()
*/
public void shutdown() {
/*
* Flag that the main thread should break out of the
*/
/*
* Interrupt the main thread, which will cause the
* monitor to break out of its sleep if it's waiting for
* loop condition to be evaluated sooner. But make some
* effort not to cause an InterruptedIOException if
* we're not even through initialization yet; we'll get
* around to shutting down soon enough.
*/
}
{
} else
throw new IllegalArgumentException(
"usage: poold [-l level]");
}
p.run();
}
/**
* The <code>utility</code> class provides various
* <code>Poold</code> related static utility methods that
* don't naturally reside on any class.
*/
static class utility {
/**
* Outputs a near-final message corresponding
* to an exception (or other throwable) to the named
* logger before causing the VM to exit. The message
* will have ERROR severity unless an instance of
* PoolsException is given, in which case the message
* will adopt the PoolsException's severity. Similarly,
* if the PoolsException specifies an exit code, it will
* be used; otherwise the default of
* <code>E_PO_FAILURE</code> will be used.
*
* @param logger Logger used to log the message
* @param t The cause. A stack trace will be affixed to
* the message.
*/
{
}
/**
* Outputs a near-final message corresponding
* to an exception (or other throwable) to the named
* logger before causing the VM to exit. The message
* will have ERROR severity unless an instance of
* PoolsException is given, in which case the message
* will adopt the PoolsException's severity. Similarly,
* if the PoolsException specifies an exit code, it will
* be used; otherwise the default of
* <code>E_PO_FAILURE</code> will be used.
*
* @param logger Logger used to log the message
* @param t The cause.
* @param showStackTrace If true, a stack trace will be
* affixed to the message.
*/
boolean showStackTrace)
{
try {
/*
* Configure the message's exception and
* severity.
*/
if (t instanceof PooldException)
((PooldException)t).getSeverity(),
t.getMessage());
else
t.getMessage());
if (showStackTrace)
if (t instanceof PooldException)
.getExitCode());
else
} catch (Exception e) {
}
}
/**
* Outputs a warning-level message to the named logger.
*
* @param logger Logger used to log the message
* @param t The cause.
* @param showStackTrace If true, a stack trace will be
* affixed to the message.
*/
boolean showStackTrace)
{
try {
/*
* Configure the message's exception and
* severity.
*/
if (t instanceof PooldException)
((PooldException)t).getSeverity(),
t.getMessage());
else
t.getMessage());
if (showStackTrace)
} catch (Exception e) {
}
}
}
}
{
{
super(message);
}
}
{
{
super(message);
}
}
/**
* The exit code which the virtual machine should exit at if
* this exception cannot be handled.
*/
private int exitCode;
/**
* The severity of this message. See <code>Severity</code>.
*/
/**
* Constructs a message with default exit code and
* <code>System.ERR</code> severity.
*/
{
}
/**
* Constructs a message with given exit code and
* <code>System.ERR</code> severity.
*/
{
}
/**
* Constructs a message with a given exit code and severity.
*/
{
super(message);
}
/**
* The exit code which the virtual machine should exit at if
* this exception cannot be handled.
*/
public int getExitCode()
{
return (exitCode);
}
{
return (severity);
}
}