root/usr/src/cmd/pools/poold/com/sun/solaris/service/locality/LocalityDomain.java
/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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 2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * ident        "%Z%%M% %I%     %E% SMI"
 *
 */

package com.sun.solaris.service.locality;

import java.util.*;

import com.sun.solaris.service.pools.*;

/**
 * A representation of the Locality Groups for a single Solaris
 * instance.
 */
public class LocalityDomain
{
        /**
         * Obtain a Locality Group snapshot based on the view
         * available to the caller.
         */
        public static final int LGRP_VIEW_CALLER = 0;

        /**
         * Obtain a Locality Group snapshot based on the view
         * of the Operating System.
         */
        public static final int LGRP_VIEW_OS = 1;

        static
        {
                System.loadLibrary("jlgrp");
        }

        /**
         * The view used to create this LocalityDomain.
         */
        private int view;

        /**
         * The cookie which represents the snapshot of locality
         * information held by the lgrp library.
         */
        private long cookie;

        /**
         * The root LocalityGroup for the LocalityDomain
         */
        private LocalityGroup root;

        /**
         * Cached value of maxLatency.
         */
        private final int maxLatency;

        /**
         * String representation of often used property.
         */
        private final static String CPU_SYS_ID = "cpu.sys_id";

        /**
         * Constructor.
         *
         * @param view to use when creating the LocalityDomain.
         * @throws Exception if there is a problem initializing the
         * lgrp snapshot.
         */
        public LocalityDomain(int view) throws Exception
        {
                this.view = view;
                cookie = jl_init(view);
                root = jl_root();
                /*
                 * The maxLatency calculation is expensive and is used
                 * every time a locality objective is examined. Since
                 * it will never change over the lifetime of a
                 * LocalityDomain, we calculate it once in the
                 * constructor and cache for future use.
                 */
                maxLatency = calcMaxLatency();
        }

        /**
         * Reclaim the resource allocated by the C proxy.
         *
         * @throws Throwable if there is a problem reclaiming the reosurces.
         */
        protected void finalize() throws Throwable
        {
                try
                {
                        close();
                }
                finally
                {
                        super.finalize();
                }
        }

        /**
         * Return the "root" LocalityGroup.
         */
        public LocalityGroup getRoot()
        {
                return (root);
        }

        /**
         * Close this LocalityDomain. Resources are reclaimed in the C
         * proxy and this LocalityDomain should never be used
         * again. None of the LocalityGroups which are referenced from
         * this LocalityDomain should be used after this method is
         * invoked. NB: jl_fini returns a success indicator which is
         * ignored as we are closing this domain.
         */
        public void close()
        {
                if (cookie != 0) {
                        jl_fini();
                        cookie = 0;
                        root = null;
                }
        }

        /**
         * Return a string representation of this instance.
         */
        public String toString()
        {
                return (root.toString());
        }

        /**
         * Return the groups in this domain to which the supplied cpus
         * belong, excluding the supplied set of groups.
         *
         * @param exclude Set of groups to be excluded.
         * @param cpus List of cpus
         *
         * @throws PoolsException if there is an error accessing the
         * cpu details.
         */
        public Set foreignGroups(Set exclude, List cpus) throws PoolsException
        {
                Iterator cpuIt = cpus.iterator();
                Set result = new HashSet();
                while (cpuIt.hasNext()) {
                        Component comp = (Component) cpuIt.next();
                        int id = (int) comp.getLongProperty(CPU_SYS_ID);
                        LocalityGroup group = getGroup(id);
                        if (group != null && exclude.contains(group) == false)
                                result.add(group);
                }
                return (result);
        }

        /**
         * Return the locality group which contains the majority of
         * the cpus in the supplied list. If more than one group
         * satisfies this criteria, then the choice of group is
         * deterministic but unspecified.
         *
         * @param cpus List of cpus to be examined.
         *
         * @throws PoolsException if there is an error accessing the
         * cpu details.
         */
        public LocalityGroup getRepresentativeGroup(List cpus)
            throws PoolsException
        {
                Iterator cpuIt = cpus.iterator();
                Map grps = new HashMap();
                while (cpuIt.hasNext()) {
                        Component comp = (Component) cpuIt.next();
                        int id = (int) comp.getLongProperty(CPU_SYS_ID);
                        LocalityGroup group = getGroup(id);
                        Integer score = (Integer) grps.get(group);
                        if (score != null) {
                                int iscore = score.intValue() + 1;
                                grps.put(group, Integer.valueOf(iscore));
                        } else {
                                grps.put(group, Integer.valueOf(1));
                        }
                }
                Iterator groupIt = grps.keySet().iterator();
                LocalityGroup centre = null;
                Integer highest = Integer.valueOf(0);
                while (groupIt.hasNext()) {
                        LocalityGroup cand = (LocalityGroup) groupIt.next();
                        Integer value = (Integer) grps.get(cand);
                        if (value.intValue() > highest.intValue()) {
                                highest = value;
                                centre = cand;
                        }
                }
                return (centre);
        }

        /**
         * Return the maximum latency between the groups in this
         * domain.
         *
         */
        private int calcMaxLatency()
        {
                int max = 0;

                Set groups = getGroups();
                Iterator outer = groups.iterator();
                while (outer.hasNext()) {
                        Iterator inner = groups.iterator();
                        LocalityGroup g1 = (LocalityGroup) outer.next();
                        while (inner.hasNext()) {
                                LocalityGroup g2 = (LocalityGroup) inner.next();
                                int latency = g1.getLatency(g2);
                                if (latency > max)
                                        max = latency;
                        }
                }
                return (max);
        }

        /**
         * Return the maximum possible latency between all locality
         * groups in this domain.
         */
        public int getMaxLatency()
        {
                return (maxLatency);
        }

        /**
         * Return the set of all LocalityGroups for this LocalityDomain.
         */
        public Set getGroups()
        {
                Set groups = new HashSet();
                groups.add(root);
                getGroups(root, groups);
                return (groups);
        }

        /**
         * Add all the descendent LocalityGroups for the supplied
         * group into the supplied set.
         *
         * @param group is the group whose descendents are processed.
         * @param descendents the set to add descendents of group.
         */
        private void getGroups(LocalityGroup group, Set descendents)
        {
                Set children = group.getChildren();

                if (! children.isEmpty()) {
                        Iterator itChild = children.iterator();
                        while (itChild.hasNext()) {
                                LocalityGroup child = (LocalityGroup) itChild.
                                    next();
                                getGroups(child, descendents);
                        }
                        descendents.addAll(children);
                }
        }

        /**
         * Return the LocalityGroup containing the supplied CPU
         * id. Search all LocalityGroups starting at the root group.
         *
         * @param cpuid is the sys-id of the CPU to search for.
         */
        public LocalityGroup getGroup(int cpuid)
        {
                LocalityGroup answer = getGroup(root, cpuid);
                return (getGroup(root, cpuid));
        }

        /**
         * Return the LocalityGroup containing the supplied CPU
         * id. Search LocalityGroups starting at the supplied group.
         *
         * @param group is the group to start searching from.
         * @param cpuid is the sys-id of the CPU to search for.
         */
        private LocalityGroup getGroup(LocalityGroup group, int cpuid)
        {
                Set children = group.getChildren();

                if (children.isEmpty()) {
                        int cpus[] = group.getCPUIDs();


                        for (int i = 0; i < cpus.length; i++)
                                if (cpus[i] == cpuid) {
                                        return (group);
                                }
                } else {
                        Iterator itGroup = children.iterator();
                        while (itGroup.hasNext()) {
                                LocalityGroup owner;
                                LocalityGroup child = (LocalityGroup) itGroup.
                                    next();
                                if ((owner = getGroup(child, cpuid)) != null)
                                        return (owner);
                        }
                }
                return (null);
        }

        /**
         * Initialise the LocalityDomain with an lgrp snapshot.
         *
         * @param view is the type of snapshot to obtain.
         */
        private native long jl_init(int view) throws Exception;

        /**
         * Release the lgrp snapshot.
         */
        private native int jl_fini();

        /**
         * Find the root LocalityGroup.
         */
        private native LocalityGroup jl_root();

}