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