#!/usr/bin/perl

#
# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# 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 2006 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
#ident	"@(#)Lgrp_api.t	1.3	06/06/08 SMI"
#

require 5.8.0;
use strict;
use warnings;

######################################################################
# Tests for Solaris::Lgrp API.
#
# This is an example script that demonstrates use of Solaris::Lgrp module.
# It can be used to test the module itself, the liblgrp library or the in-kernel
# implementation.
######################################################################

#                       Tests to run
use Test::More tests => 33;

# Verify that we can load the module
BEGIN { use_ok('Solaris::Lgrp') };

use Solaris::Lgrp ':ALL';

my ($home, $fail);

######################################################################
# Verify that lgrp_init() works.
##
my $c = Solaris::Lgrp->new(LGRP_VIEW_OS);
ok($c, 'lgrp_init') or die("lgrp_init: $!");
#
######################################################################

######################################################################
# root should have ID 0.
##
my $root = $c->root;
is($root, 0, 'root should have id zero');

#
######################################################################
# Verify lgrp_nlgrps()
##
my $nlgrps = $c->nlgrps;
ok($nlgrps, 'lgrp_nlgrps') or
    diag("lgrp_nlgrps: $!");

my $is_numa = ($nlgrps > 1);

my @lgrps = $c->lgrps;
ok(scalar @lgrps, 'Can get lgrps list') or
    diag("lgrp_lgrps: $!");

is(scalar @lgrps, $nlgrps, 'lgrp_nlgrps() should match number of lgrps');

######################################################################
# All root children should have root as their one and only one parent
##
$fail = 0;
my (@children) = $c->children($root);
my @leaves = $c->leaves;
ok(@leaves, 'There are some leaves');

cmp_ok(@children, '<=', @leaves, 'Root should have nchildren <= nleaves');
my @parents;

foreach my $l (@children) {
    (@parents) = $c->parents($l) or
	diag("lgrp_parents: $!");
    my $nparents = @parents;
    my ($parent, @rest) = @parents;
    $fail++ if $parent != $root;
    $fail++ unless $nparents == 1;
}
is($fail, 0, 'correct parents for children');

######################################################################
# Each lgrp other than root should have a single parent and
# root should have no parents.
##

$fail = 0;
foreach my $l (lgrp_lgrps($c)) {
    next if $l == $root;
    my (@parents) = $c->parents($l) or
	diag("lgrp_parents: $!");
    my $nparents = @parents;
    $fail++ unless $nparents == 1;
}
is($fail, 0, 'All non-leaf lgrps should have single parent');

@parents = $c->parents($root);
ok(!@parents, 'root should have no parents');
#
#######################################################################

######################################################################
# Lgrp affinity tests.
#######################

######################################################################
# lgrp_affinity-set should change home lgrp.
##
SKIP: {
    skip 'Test only valid on NUMA platform', 1 unless $is_numa;
    my $leaf = $leaves[0];	# Pickup any non-root lgrp.
    $home = $c->home(P_PID, P_MYID);

    # Pickup any lgrp not equal to the current one.
    my $lgrp = ($home == $root ? $leaf : $root);
    # Set affinity to the new lgrp.
    $c->affinity_set(P_PID, P_MYID, $lgrp, LGRP_AFF_STRONG) or
	diag("lgrp_affinity_set(): $!");
    # Our home should change to a new lgrp.
    $home = $c->home(P_PID, P_MYID);
    is($home, $lgrp, 'Home lgrp should change after strong affinity is set');
    # Drop affinity to the lgrp.
    $c->affinity_set(P_PID, P_MYID, $lgrp, LGRP_AFF_NONE) or
	diag("lgrp_affinity_set(): $!");
}

######################################################################
# Should be able to set affinity to any legal value
##

my @affs = (LGRP_AFF_WEAK, LGRP_AFF_STRONG, LGRP_AFF_NONE);

foreach my $aff (@affs) {
    $c->affinity_set(P_PID, P_MYID, $root, $aff) or
	diag("lgrp_affinity_set(): $!");
    my $affinity = $c->affinity_get(P_PID, $$, $root);
    is($affinity, $aff, "affinity should be $aff");
}

#
######################################################################

######################################################################
# Root should have non-zero CPUs and memory size
# Also, its memory size should be consistent with the one reported by
# sysconfig.
##
my @rcpus = $c->cpus($root, LGRP_CONTENT_HIERARCHY) or
    die("lgrp_cpus: $!");
my $ncpus = @rcpus;
ok($ncpus, 'there are CPUs in the system');

my $memsize = $c->mem_size($root,
			    LGRP_MEM_SZ_INSTALLED,
			   LGRP_CONTENT_HIERARCHY) or
    diag("lgrp_mem_size(): $!");

ok($memsize, 'memory size is non-zero');
#
######################################################################

######################################################################
# The cookie should not be stale
is($c->stale, 0, 'Cookie should not be stale');
#
######################################################################

######################################################################
# Latency should be non-zero.
my $latency = lgrp_latency($root, $root);
ok(defined $latency, 'lgrp_latency() is working') or
    diag("lgrp_latency: $!");

my $latency1 = $c->latency($root, $root);
ok(defined $latency1, 'lgrp_latency_cookie() is working') or
    diag("lgrp_latency_cookie: $!");

is($latency, $latency1, 'Latencies should match');
#
######################################################################

######################################################################
# Verify latency matrix.
##
SKIP: {
    skip 'Test only valid on NUMA platform', 9 unless $is_numa;

    cmp_ok($latency, '>', 0, "Latency from root to self should be positive");
    my $latencies;
    my $min_latency = 10000;
    my $max_latency = 0;
    my $badlatency = 0;
    my $assymetrical = 0;
    my $diagonalmin = 0;
    my $badself = 0;
    my $nlatencies;

    foreach my $l1 (@lgrps) {
	foreach my $l2 (@lgrps) {
	    $latencies->{$l1}{$l2} = $c->latency($l1, $l2);
	    $nlatencies++ if $latencies->{$l1}{$l2};
	}
    }

    # There should be at least some lgroups which have latencies.
    my @d_lgrps = grep { defined $latencies->{$_}{$_} } @leaves;
    ok(@d_lgrps, 'There should be at least some lgroups which have latencies');

    # All diagonal latencies should be the same.
    my $lat_diag_lgrp = $d_lgrps[0];
    my $lat_diag = $latencies->{$lat_diag_lgrp}{$lat_diag_lgrp};
    my @badlatencies = grep { $latencies->{$_}{$_} != $lat_diag } @d_lgrps;
    is(scalar @badlatencies, 0, 'All diagonal latencies should be the same') or
      diag("diagonal latency: $lat_diag; bad latencies: @badlatencies");

    my %l_cpus;
    my %l_mem;
    my $lgrps_nomem;
    my $lgrps_nocpus;

    foreach my $l1 (@lgrps)  {
	$l_cpus{$l1} = scalar $c->cpus($l1, LGRP_CONTENT_HIERARCHY);
	$l_mem{$l1}  = $c->mem_size($l1, LGRP_MEM_SZ_INSTALLED,
				   LGRP_CONTENT_HIERARCHY);
	$lgrps_nomem++ unless $l_mem{$l1};
	$lgrps_nocpus++ unless $c->cpus($l1, LGRP_CONTENT_HIERARCHY);
    }

    # Verify latencies consistency
    foreach my $l1 (@lgrps) {
	# Can't get latency if source doesn't have CPUs
	next unless $l_cpus{$l1};
	my $self_latency = $latencies->{$l1}{$l1};
	$lat_diag = $self_latency if $self_latency;

	foreach my $l2 (@lgrps) {
	    # Can't get latenciy if destination doesn't have memory
	    next unless $l_mem{$l2};

	    if (! $latencies->{$l1}{$l2}) {
		$badlatency++;
		diag("Invalid latency between $l1 and $l2");
		next;
	    }

	    $max_latency = $latencies->{$l1}{$l2} if 
		$latencies->{$l1}{$l2} > $max_latency;
	    $min_latency = $latencies->{$l1}{$l2} if
		$latencies->{$l1}{$l2} < $min_latency;

	    # Latencies should be symmetrical but only if they are valid.
	    if ($latencies->{$l2}{$l1} &&
		$latencies->{$l1}{$l2} != $latencies->{$l2}{$l1}) {
		$assymetrical++;
		diag("latency($l1, $l2) != latency($l2, $l1)");
	    }

	    $diagonalmin++ if $c->isleaf($l1) && $c->isleaf($l2) &&
		$self_latency && $self_latency > $latencies->{$l1}{$l2};
	}
    }

  SKIP: {
	skip 'Symmetry test only valid if all lgroups have memory and CPUs',
	  1 if $lgrps_nomem || $lgrps_nocpus;
    	is($assymetrical,  0, 'Latencies should be symmetrical');
    }

    is($diagonalmin, 0, 'Latency should be minimal on diagonals');
    is($badlatency, 0, 'Latency should be defined');
    is($max_latency, $latencies->{$root}{$root},
       'Root should have maximum latencies');
    cmp_ok($min_latency, '>', 0, 'Minimum latency should be positive') if
	$nlatencies;
    cmp_ok($min_latency, '<=', $max_latency,
	   'Minimum latency should be less then maximum') if $nlatencies;
}

######################################################################
# Verify lgrp_resources API
##
SKIP: {
    skip 'lgrp_resources() is not supported', 3 if
	((LGRP_VER_CURRENT == 1) || !$is_numa);

    my @lgrps_c = $c->resources($root, LGRP_RSRC_CPU);
    ok(scalar @lgrps_c, 'there are CPU resources in the system');
    $fail = 0;
    my $nc = 0;
    foreach my $l (@lgrps_c) {
	$fail++ unless $c->isleaf($l);
	my @cpu_l = $c->cpus($l, LGRP_CONTENT_DIRECT);
	$nc += @cpu_l;
    }
    is($fail, 0, 'Each lgrp containing CPU resources should be leaf');
    is($nc, $ncpus, 'Number of CPUs should match');
}

#
######################################################################
# THE END!
#########
