745326
#!/usr/bin/perl
745326
745326
# * Copyright (c) 2009-2011 Mellanox Technologies Ltd. All rights reserved.
745326
# * Copyright (c) 2009-2011 System Fabric Works, Inc. All rights reserved.
745326
# *
745326
# * This software is available to you under a choice of one of two
745326
# * licenses.  You may choose to be licensed under the terms of the GNU
745326
# * General Public License (GPL) Version 2, available from the file
745326
# * COPYING in the main directory of this source tree, or the
745326
# * OpenIB.org BSD license below:
745326
# *
745326
# *     Redistribution and use in source and binary forms, with or
745326
# *     without modification, are permitted provided that the following
745326
# *     conditions are met:
745326
# *
745326
# *	- Redistributions of source code must retain the above
745326
# *	  copyright notice, this list of conditions and the following
745326
# *	  disclaimer.
745326
# *
745326
# *	- Redistributions in binary form must reproduce the above
745326
# *	  copyright notice, this list of conditions and the following
745326
# *	  disclaimer in the documentation and/or other materials
745326
# *	  provided with the distribution.
745326
# *
745326
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
745326
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
745326
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
745326
# * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
745326
# * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
745326
# * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
745326
# * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
745326
# * SOFTWARE.
745326
#
745326
745326
use warnings;
745326
use strict;
745326
745326
use File::Basename;
745326
use File::Path qw(make_path);
745326
use Getopt::Long;
745326
745326
my $help = 0;
745326
my $no_persist = 0;
745326
my $debug = 0;
745326
my $force = 0;
745326
my $linkonly = 0;
745326
my $parms = "/sys/module/rdma_rxe/parameters";
745326
my $modprobe_opt = "";
745326
my $modprobe_checked = "0";
745326
my $persistence_path = "/var/lib/rxe";
745326
my $persistence_file = "${persistence_path}/rxe";
745326
my $num_persistent = 0;
745326
my $sys = "/sys/module/rdma_rxe/parameters";
745326
my %rxe_names;
745326
my @rxe_array;
745326
my %eth_names;
745326
my @eth_list;
745326
my %eth_driver;
745326
my %link_state;
745326
my %link_speed;
745326
my %eth_mtu;
745326
my %ipv4_addr;
745326
my %rxe_mtu;
745326
my @persistence_array;
745326
my %persistence_hash;
745326
my @mlx4_port;
745326
my @mlx4_ether;
745326
my @roce_list;
745326
745326
# Read a file and return its contents as a string.
745326
sub read_file {
745326
    my $filename = shift;
745326
    my $result = "";
745326
745326
    if (open(FILE, $filename)) {
745326
	$result = <FILE>;
745326
	close FILE;
745326
    }
745326
    return $result;
745326
}
745326
745326
#get mapping between rxe and eth devices
745326
sub get_names {
745326
    my $i = 0;
745326
    
745326
    foreach my $rxe (glob("/sys/class/infiniband/rxe*")) {
745326
	$rxe = basename($rxe);
745326
	my $eth = read_file("/sys/class/infiniband/$rxe/parent");
745326
	chomp($eth);
745326
	
745326
	if (($eth =~ /[\w]+[\d]/)
745326
	    && ($rxe =~ /rxe[0123456789]/)) {
745326
	    
745326
	    # hash ethername to rxename
745326
	    $rxe_names{$eth} = $rxe;
745326
	    $rxe_array[$i++] = $rxe;
745326
	    
745326
	    # hash rxename to ethername
745326
	    $eth_names{$rxe} = $eth;
745326
	}
745326
    }
745326
}
745326
745326
# get list of Mellanox RoCE ports
745326
sub get_mlx4_list {
745326
    my $i = 0;
745326
745326
    foreach my $mlx4 (glob("/sys/class/infiniband/mlx4_*")) {
745326
	$mlx4 = basename($mlx4);
745326
	foreach my $port (glob("/sys/class/infiniband/$mlx4/ports/*")) {
745326
	    $port = basename($port);
745326
	    my $link = read_file("$port/link_layer");
745326
	    chomp($link);
745326
745326
	    if ($link =~ "Ethernet") {
745326
		$roce_list[$i++] = "$mlx4:$port";
745326
	    }
745326
	}
745326
    }
745326
}
745326
745326
#collect per device information
745326
sub get_dev_info {
745326
    my @list;
745326
    my @fields;
745326
    my @lines;
745326
    my $line;
745326
    my $eth;
745326
    my $drv;
745326
    my $np;
745326
    my $i = 0;
745326
    my $j = 0;
745326
745326
    get_mlx4_list();
745326
745326
    my @my_eth_list = ();
745326
    foreach my $my_eth_dev (glob("/sys/class/net/*")) {
745326
       $my_eth_dev = basename($my_eth_dev);
745326
          if ($my_eth_dev ne "bonding_masters"){
745326
             my $my_dev_type = read_file("/sys/class/net/${my_eth_dev}/type");
745326
             chomp($my_dev_type);
745326
             if ($my_dev_type == "1") {
745326
                push(@my_eth_list, "$my_eth_dev");
745326
             }
745326
          }
745326
    }
745326
745326
    @list = @my_eth_list;
745326
    foreach $eth (@list) {
745326
	chomp($eth);
745326
745326
	$eth_list[$i++] = $eth;
745326
745326
	@lines = `ethtool -i $eth`;
745326
	foreach $line (@lines) {
745326
	    chomp($line);
745326
745326
	    @fields = split(/\s+/, $line);
745326
	    chomp($fields[0]);
745326
745326
	    if ($fields[0] =~ /driver:/) {
745326
		$drv = $fields[1];
745326
		$eth_driver{$eth} = $drv;
745326
745326
		if ($drv =~ /mlx4_en/ && scalar(@roce_list) > 0 ) {
745326
		    $eth_names{$roce_list[$j++]} = $eth;
745326
		}
745326
	    }
745326
	}
745326
745326
	# get link status
745326
	$link_state{$eth} = "";
745326
	$link_speed{$eth} = "";
745326
745326
	@lines = `ethtool $eth`;
745326
	foreach $line (@lines) {
745326
	    chomp($line);
745326
745326
	    @fields = split(/:/, $line);
745326
	    if (defined($fields[1])) {
745326
		    $fields[1] =~ s/^\s+//g;
745326
		    if ($fields[0] =~ "Link detected") {
745326
			$link_state{$eth} = $fields[1];
745326
		    }
745326
	    }
745326
	    elsif ($line =~ "10000baseT") {
745326
		$link_speed{$eth} = "10GigE";
745326
	    }
745326
	}
745326
745326
	$ipv4_addr{$eth} = "            ";
745326
	$eth_mtu{$eth} = "";
745326
745326
	@lines = `ip addr show $eth`;
745326
	foreach $line (@lines) {
745326
		# get IP address
745326
		if ($line =~ /inet /) {
745326
			$line =~ s/^\s+inet ([0-9.]+)\//$1 /g;
745326
			@fields = split(/\s+/, $line);
745326
			$ipv4_addr{$eth} = $fields[0];
745326
		}
745326
745326
		# get ethernet mtu
745326
		if ($line =~ /mtu /) {
745326
			$line =~ s/^.*mtu //g;
745326
			@fields = split(/\s+/, $line);
745326
			$eth_mtu{$eth} = $fields[0];
745326
		}
745326
    }
745326
    }
745326
745326
    # get rxe mtu
745326
    foreach my $rxe (@rxe_array) {
745326
	
745326
	@lines = `ibv_devinfo -d $rxe`;
745326
	foreach $line (@lines) {
745326
	    if ($line =~ "active_mtu") {
745326
		$line =~ s/^\s+active_mtu:\s+//g;
745326
		chomp($line);
745326
745326
		$rxe_mtu{$rxe} = $line;
745326
	    }
745326
	}
745326
	$rxe_mtu{$rxe} = "(?)" if (!$rxe_mtu{$rxe});
745326
    }
745326
}
745326
745326
# return string or the string "###" if string is all whitespace
745326
sub set_field {
745326
    my $fld = $_[0];
745326
745326
    if (defined($fld) && $fld =~ /\S/) {
745326
        return $fld;
745326
    } else {
745326
        return "###";
745326
    }
745326
}
745326
745326
# format status output into fixed width columns
745326
sub status_print {
745326
    my @fields;
745326
    my $field;
745326
    my @flen = ();
745326
    my $num_fields = 0;
745326
    my $i;
745326
    my $pad;
745326
    my $line;
745326
745326
    # one pass to size the columns
745326
    foreach $line (@_) {
745326
	@fields = split(/\s+/, $line);
745326
	$i = 0;
745326
	foreach $field (@fields) {
745326
	    if (!defined($flen[$i])) {
745326
		$flen[$i] = length($field);
745326
	    }
745326
	    else {
745326
		$flen[$i] = max($flen[$i], length($field));
745326
	    }
745326
	    $i++;
745326
	}
745326
745326
	if ($i > $num_fields) {
745326
	    $num_fields = $i;
745326
	}
745326
    }
745326
745326
    # one pass to print
745326
    foreach $line (@_) {
745326
	print "  ";
745326
	@fields = split(/\s+/, $line);
745326
	for ($i = 0; $i < $num_fields; $i++) {
745326
	    if (defined($fields[$i])) {
745326
	        $pad = $flen[$i] - length($fields[$i]) + 2;
745326
	    }
745326
	    else {
745326
	        $pad = $flen[$i] + 2;
745326
	    }
745326
	    if (defined($fields[$i]) && ($fields[$i] ne "###")) {
745326
		print "$fields[$i]";
745326
	    }
745326
	    else {
745326
		print "   ";
745326
	    }
745326
	    printf("%*s", $pad, "");
745326
	}
745326
	print "\n";
745326
    }
745326
}
745326
745326
# check driver load status
745326
sub check_module_status {
745326
    if (-e $sys) {
745326
	return 0;
745326
    } else {
745326
	return 1;
745326
    }
745326
}
745326
745326
# print driver load status and ethertype for rdma_rxe and rdma_rxe_net
745326
sub show_module_status {
745326
    print "rdma_rxe module not loaded\n" if (!(-e $sys));
745326
}
745326
745326
# print rxe status
745326
sub do_status {
745326
    my $instance = $_[0];
745326
    my $ln = 0;
745326
    my @outp;
745326
    my $rxe;
745326
    my $rmtu;
745326
745326
    get_names();
745326
    get_dev_info();
745326
    show_module_status();
745326
745326
    $outp[$ln++] = "Name\tLink\tDriver\t\tSpeed\tNMTU\tIPv4_addr\tRDEV\tRMTU";
745326
745326
    foreach my $eth (@eth_list) {
745326
745326
	# handle case where rxe_drivers are not loaded
745326
	if (defined($rxe_names{$eth})) {
745326
		$rxe = $rxe_names{$eth};
745326
		$rmtu = $rxe_mtu{$rxe};
745326
	}
745326
	else {
745326
		$rxe = "";
745326
		$rmtu = "";
745326
	}
745326
745326
	if ((!defined($instance) 
745326
	     && (($linkonly == 0) || ($link_state{$eth} =~ "yes")))
745326
	    || (defined($instance) && ($rxe =~ "$instance"))) {
745326
	    $outp[$ln] =  set_field("$eth");
745326
	    $outp[$ln] .= "\t";
745326
	    $outp[$ln] .= set_field("$link_state{$eth}");
745326
	    $outp[$ln] .= "\t";
745326
	    $outp[$ln] .= set_field(exists($eth_driver{$eth}) ? $eth_driver{$eth} : "");
745326
	    $outp[$ln] .= "\t";
745326
	    $outp[$ln] .= set_field("$link_speed{$eth}");
745326
	    $outp[$ln] .= "\t";
745326
	    $outp[$ln] .= set_field("$eth_mtu{$eth}");
745326
	    $outp[$ln] .= "\t";
745326
	    $outp[$ln] .= set_field("$ipv4_addr{$eth}");
745326
	    $outp[$ln] .= "\t";
745326
	    $outp[$ln] .= set_field("$rxe");
745326
	    $outp[$ln] .= "\t";
745326
	    $outp[$ln] .= set_field("$rmtu");
745326
	    $ln++;
745326
	}
745326
    }
745326
745326
    status_print(@outp);
745326
}
745326
745326
# read file containing list of ethernet devices into a list
745326
sub populate_persistence {
745326
    my $i = 0;
745326
    
745326
    open FILE, $persistence_file;
745326
    while(<FILE>) {
745326
	my $line = $_;
745326
	chomp($line);
745326
	$line =~ s/^\s+//g;
745326
	if ($line =~ /[\w]+[\d]/) {
745326
	    # in case we add fields later
745326
	    my ($eth, $cruft) = split(/\s+/, $line, 2);
745326
	    if ($eth =~ /^[\w]+[\d]/) {
745326
		$persistence_array[$i] = $eth;
745326
		$persistence_hash{$eth} = $i++;
745326
	    }
745326
	}
745326
    }
745326
    close FILE;
745326
745326
    $num_persistent = $i;
745326
}
745326
745326
# print out list of ethernet devices to file
745326
sub commit_persistent {
745326
    my $i;
745326
    my $eth;
745326
745326
    open(PF, ">$persistence_file");
745326
    
745326
    for ($i = 0; $i < $num_persistent; $i++) {
745326
	$eth = $persistence_array[$i];
745326
	if ($eth =~ /[\w]+[\d]/) {
745326
	    print(PF "$persistence_array[$i]\n");
745326
	}
745326
    }
745326
745326
    close(PF);
745326
}
745326
745326
sub delete_persistent {
745326
    my $eth = $_[0];
745326
    
745326
    if (defined($persistence_hash{$eth})) {
745326
	$persistence_array[$persistence_hash{$eth}] = "";
745326
    }
745326
}
745326
745326
sub add_persistent {
745326
    my $eth = $_[0];
745326
745326
    # Is this one already in the persistence list?
745326
    if (!defined($persistence_hash{$eth})) {
745326
	$persistence_array[$num_persistent] = $eth;
745326
	$persistence_hash{$eth} = $num_persistent;
745326
	$num_persistent++;
745326
    }
745326
}
745326
745326
# add new rxe device to eth if not already up
745326
sub rxe_add {
745326
    my $eth = $_[0];
745326
745326
    if (!($eth =~ /[\w]+[\d]/)) {
745326
	print "eth_name ($eth) looks bogus\n";
745326
	return;
745326
    }
745326
745326
    if (!defined($rxe_names{$eth})) {
745326
	system("echo '$eth' > $parms/add");
745326
    }
745326
    if (!$no_persist) {
745326
	add_persistent($eth);
745326
	commit_persistent();
745326
    }
745326
}
745326
745326
sub rxe_remove {
745326
    my $arg2 = $_[0];
745326
    my $rxe;
745326
    my $eth;
745326
745326
    print "remove $arg2\n"  if ($debug > 0);
745326
745326
    if ($arg2 =~ /[\w]+[\d]/) {
745326
	$eth = $arg2;
745326
	$rxe = $rxe_names{$eth};
745326
    }
745326
    elsif ($arg2 =~ /rxe[0123456789]/) {
745326
	$rxe = $arg2;
745326
	$eth = $eth_names{$rxe};
745326
    }
745326
    elsif ($arg2 eq "all") {
745326
	$rxe = "all";
745326
    }
745326
745326
    if (($rxe eq "all") || ($rxe =~ /^rxe[0123456789]/)) {
745326
	my $cmd = "echo '$rxe' > $parms/remove";
745326
	#print "$cmd\n";
745326
	system($cmd);
745326
	if (!$no_persist) {
745326
	    if ($rxe eq "all") {
745326
		unlink($persistence_file);
745326
	    }
745326
	    elsif ($eth =~/[\w]+[\d]/) {
745326
		delete_persistent($eth);
745326
		commit_persistent();
745326
	    }
745326
	    else {
745326
		print "Warning: Unable to resolve ethname; "
745326
		    . "instance may persist on restart\n";
745326
	    }
745326
	}
745326
    }
745326
    else {
745326
	print "rxe instance $rxe not found\n";
745326
    }
745326
}
745326
745326
sub get_devinfo {
745326
    my $rxe = $_[0];
745326
745326
    my $cmd = "ibv_devinfo -d $rxe";
745326
    return `$cmd`;
745326
}
745326
745326
# allow unsupported modules to load in SLES11 if allowed
745326
sub modprobe {
745326
    my $module = $_[0];
745326
    my $opts = $_[1];
745326
    my @lines;
745326
    my $line;
745326
745326
    if ($modprobe_checked == "0") {
745326
	@lines = `modprobe -c`;
745326
	foreach $line (@lines) {
745326
	    if ($line =~ /^allow_unsupported_modules  *0/) {
745326
		$modprobe_opt = " --allow-unsupported-modules ";
745326
		last;
745326
	    }
745326
	}
745326
	$modprobe_checked = "1";
745326
    }
745326
745326
    if (!defined($opts)) {
745326
	$opts = "";
745326
    }
745326
745326
    system("modprobe $modprobe_opt $module $opts");
745326
}
745326
745326
# bring up rxe
745326
sub do_start {
745326
    my $proto_str = "";
745326
745326
    system("mkdir -p $persistence_path");
745326
    system("touch $persistence_file");
745326
745326
    modprobe("ib_core");
745326
    modprobe("ib_uverbs");
745326
    modprobe("rdma_ucm");
745326
    modprobe("rdma_rxe");
745326
745326
    populate_persistence();
745326
    system("udevadm control --reload");
745326
745326
    foreach my $eth (@persistence_array) {
745326
	rxe_add($eth);
745326
    }
745326
745326
    get_names();
745326
745326
    foreach my $rxe (@rxe_array) {
745326
	my $stat = get_devinfo($rxe);
745326
	if ($stat =~ "PORT_DOWN") {
745326
		my $cmd = "ip link set $eth_names{$rxe} up";
745326
		system($cmd);
745326
	}
745326
    }
745326
745326
}
745326
745326
# check if argument is an integer
745326
sub is_integer {
745326
    defined $_[0] && $_[0] =~ /^[+-]?\d+$/;
745326
}
745326
745326
# remove all rxe devices and unload drivers
745326
sub do_stop {
745326
    my $rxe;
745326
745326
    foreach $rxe (@rxe_array) {
745326
	system("echo '$rxe' > $sys/remove");
745326
    }
745326
745326
    if (-e $sys) {
745326
	system("rmmod rdma_rxe");
745326
    }
745326
745326
    if (-e $sys) {
745326
	print "unable to unload drivers, reboot required\n";
745326
    }
745326
}
745326
745326
sub do_debug {
745326
    my $arg2 = $_[0];
745326
    my $debugfile = "$parms/debug";
745326
    chomp($arg2);
745326
745326
    if (!(-e "$debugfile")) {
745326
	print "Error: debug is compiled out of this rxe driver\n";
745326
	return;
745326
    }
745326
745326
    if    ($arg2 eq "on")  { system("echo '31' > $debugfile"); }
745326
    elsif ($arg2 eq "off") { system("echo '0'  > $debugfile"); }
745326
    elsif ($arg2 eq "0")   { system("echo '0'  > $debugfile"); }
745326
    elsif ($arg2 eq "")    { }
745326
	elsif ($arg2 ge "0" && $arg2 le "31") {
745326
	    system("echo '$arg2' > $debugfile");
745326
	}
745326
	else {
745326
	    print "unrecognized debug cmd ($arg2)\n";
745326
	}
745326
745326
    my $current = read_file($debugfile);
745326
    chomp($current);
745326
    if ($current > 0) {
745326
	print "Debug is ON ($current)\n";
745326
    }
745326
    elsif ($current == 0) {
745326
	print "Debug is OFF\n";
745326
    }
745326
    else {
745326
	print "Unrecognized debug value\n";
745326
    }
745326
}
745326
745326
sub max {
745326
    my $a = $_[0];
745326
    my $b = $_[1];
745326
    return $a if ($a > $b);
745326
    return $b;
745326
}
745326
745326
# show usage for rxe_cfg
745326
sub usage {
745326
    print "  Usage:\n";
745326
    print "    rxe_cfg [options] start|stop|status|persistent\n";
745326
    print "    rxe_cfg debug on|off|<num>\n";
745326
    print "    rxe_cfg [-n] add <ndev>\n";
745326
    print "    rxe_cfg [-n] remove <ndev>|<rdev>\n";
745326
    print "\n";
745326
    print "    <ndev> = network device e.g. eth3\n";
745326
    print "    <rdev> = rdma device e.g. rxe1\n";
745326
    print "\n";
745326
    print "  Options:\n";
745326
    print "    -h: print this usage information\n";
745326
    print "    -n: do not make the configuration action persistent\n";
745326
    print "    -v: print additional debug output\n";
745326
    print "    -l: show status for interfaces with link up\n";
745326
    print "    -p <num>: (start command only) - set ethertype\n";
745326
}
745326
745326
sub main {
745326
    GetOptions(
745326
	   "-h"          => \$help,
745326
	   "--help"      => \$help,
745326
	   "-n"          => \$no_persist,
745326
	   "-v:+"        => \$debug,
745326
	   "-f"          => \$force,
745326
	   "-l"          => \$linkonly,
745326
	   );
745326
745326
    my $arg1 = $ARGV[0];
745326
    my $arg2 = $ARGV[1];
745326
    my $arg3 = $ARGV[2];
745326
745326
    # status is the default
745326
    if (!defined($arg1) || ($arg1 =~ /status/)) {
745326
        do_status($arg2);
745326
        exit;
745326
    }
745326
745326
    if ($help) {
745326
        usage();
745326
        exit;
745326
    }
745326
745326
    # stuff that does not require modules to be loaded
745326
    if    ($arg1 eq "help")       { usage(); exit; }
745326
    elsif ($arg1 eq "start")      { do_start(); do_status(); exit; }
745326
    elsif ($arg1 eq "persistent") { system("cat $persistence_file"); exit; }
745326
745326
745326
    # can't do much else, bail if modules aren't loaded
745326
    if (check_module_status()) {
745326
	exit;
745326
    }
745326
745326
    # create persistence file if necessary
745326
    make_path($persistence_path);
745326
    if (!(-e $persistence_file)) {
745326
        `touch $persistence_file`;
745326
    }
745326
745326
    # Get full context of the configuration
745326
    populate_persistence();
745326
    get_names();
745326
    get_dev_info();
745326
745326
    # Stuff that requires the rdma_rxe module to be loaded
745326
    if    ($arg1 eq "stop")   { do_stop(); 	   exit; }
745326
    elsif ($arg1 eq "debug")  { do_debug($arg2);   exit; }
745326
    elsif ($arg1 eq "add")    { rxe_add($arg2);    exit; }
745326
    elsif ($arg1 eq "remove") { rxe_remove($arg2); exit; }
745326
    elsif ($arg1 eq "help")   { usage();	   exit; }
745326
}
745326
745326
main();
745326
745326
exit;