summaryrefslogtreecommitdiffstats
path: root/loader/lsznet.raw
blob: 5b3df91e20e6c5d99b1903a0d9c3079cf233d1e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
#! /bin/bash

# lsznet.raw: list sensible network device hardware setups for Linux on s390(x)
# Copyright (C) IBM Corp. 2008,2009
# Author: Steffen Maier <maier@de.ibm.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

readonly SYSFS=/sys
# DEBUG=0 turns off debugging. >=1 means increasing debugging.
readonly DEBUG=0

# nothing to be changed below here

export TEXTDOMAIN=lsznet.raw

readonly CMD=${0##*/}

function error() {
    echo $"$CMD: ERROR: $*" 1>&2
    exit 1;
}

# currently requires bash version 3.0 or later
# (this seems reasonable since bash-3.0 has been shipped with
#  RHEL 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7;
#  bash-3.1 with RHEL 5.0, 5.1; and bash-3.2 with RHEL 5.2)
if [ ${BASH_VERSINFO[0]} -lt 3 -o \
    ${BASH_VERSINFO[1]} -lt 0 ]; then
    error $"only works with BASH version 3.0 or later (current version used is ${BASH_VERSION})"
fi

# the script was designed for Linux kernel 2.6 and might work with newer ones
kernel_version=$(uname -r)
IFS=.
read krn_ver krn_patch krn_foo <<< "$kernel_version"
unset IFS
if [ $krn_ver -lt 2 -o $krn_patch -lt 6 ]; then
    error $"only works for kernel versions 2.6 or probably later"
fi

# The following combinations of control unit type and model were taken from the
# Linux network device drivers for s390 on 2008-06-09 from Linux 2.6.25.4.
# The list (among other things) should be adapted, if any of those device
# drivers start supporting different CU types/models.
# (Alternatively, the list could be generated by reading the modaliases
#  directly out of the device driver modules:
#  modinfo qeth/cu3088 | fgrep 'alias:'
#  However, this would still require a list of device driver modules.)
readonly -a CU=(
    1731/01
    1731/05
    3088/08
    3088/1f
    3088/1e
    3088/01
    3088/60
    3088/61
)

# $CU_CARDTYPE array is the only one which may contain entries with spaces
readonly -a CU_CARDTYPE=(
    "OSA (QDIO)"
    "HiperSocket"
    "CTC adapter"
    "escon channel"
    "ficon channel"
    "LCS p390"
    "LCS OSA"
    "LCS CLAW"
)

readonly -a CU_DEVDRV=(
    qeth
    qeth
    ctc
    ctc
    ctc
    lcs
    lcs
    lcs
)

readonly -a CU_DEVNAME=(
    eth
    hsi
    ctc
    ctc
    ctc
    eth
    eth
    eth
)

readonly -a CU_GROUPCHANNELS=(
    3
    3
    2
    2
    2
    2
    2
    2
)
readonly -a CHPIDTYPES=(
    [10]=OSE
    [11]=OSD
    [24]=IQD
)

readonly PREFIXFORMAT=[[:xdigit:]]*
readonly SSIDFORMAT=[0-3]
readonly BUSIDFORMAT=[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]
readonly IDFORMAT=$PREFIXFORMAT.$SSIDFORMAT.$BUSIDFORMAT
readonly SUBCHANNEL_TYPE_IO=0

function debug() {
    level=$1
    shift
    [ $DEBUG -ge $level ] && echo "$*" 1>&2
}

# Searches for a match of argument 1 on the array $CU and sets $cu_idx
# to the matched array index on success.
# Returns 0 on success, 1 on failure.
function search_cu() {
    local scu=$1
    local i
    for ((i=0; i < ${#CU[@]}; i++)); do
        if [ $scu == ${CU[i]} ]; then
            cu_idx=$i
            return 0
        fi
    done
    return 1
}

# Returns symbolic name of CHPID type in $chpidtype_symbolic,
# if an entry in the array $CHPIDTYPES has been found at index of argument 1.
# Returns "?" otherwise.
# Always succeeds and returns 0.
function search_chpt() {
    local chpidtype_number=$1
    chpidtype_symbolic=${CHPIDTYPES[$chpidtype_number]}
    if [ "$chpidtype_symbolic" == "" ]; then
        chpidtype_symbolic="?"
    fi
    return 0
}

# build_list:
#
# Prints list on standard output consisting of all subchannels and
# ccwdevices whose control unit type/model match supported network
# device types on s390.  Each matching entry is accompanied with
# (almost all) corresponding attributes.
#
function build_list() {
    # use /sys/devices/css*/ for startpath
    readonly STARTPATH=$SYSFS/devices
    # change to base directory so path globbing length with find is minimal
    cd $STARTPATH
    # fail out gracefully, if there is not expected sysfs environment
    # (could even fail out near the top, if $(uname -m) != s390x)
    csses=css$PREFIXFORMAT
    for d in $csses; do
        [ -d $d ] || exit
    done
    find $csses -name "$IDFORMAT" |
    while read dir; do
        debug 6 " examining sysfs directory $dir"
        # must not use $...FORMAT (file globs) here since this is a regex:
        [[ "$dir" =~ ^css\([[:xdigit:]]+\)/\([[:xdigit:]]+\\.[0-3]\\.[[:xdigit:]]{4}\)/\([[:xdigit:]]+\\.[0-3]\\.[[:xdigit:]]{4}\)$ ]]
        case $? in
            0)
                # string matched the pattern
                debug 6 " ${BASH_REMATCH[@]}"
                prefix=${BASH_REMATCH[1]}
                subch=${BASH_REMATCH[2]}
                devbusid=${BASH_REMATCH[3]}
                subch_p=css$prefix/$subch
                dev_p=$subch_p/$devbusid
                debug 6 " $subch_p $dev_p"
                ;;
            1)
                # string did not match the pattern
                continue
                ;;
            2)
                error $"syntax error in regex of match operator =~, code needs to be fixed"
                ;;
            *)
                error $"unexpected return code of regex match operator =~, code needs to be fixed"
                ;;
        esac
        debug 5 " sysfs directory matched regex $dir"
        # skip non-I/O-subchannels, i.e. chsc and message subchannels
	if [ -f $subch_p/type ]; then
	    read type < $subch_p/type
            if [ $type != $SUBCHANNEL_TYPE_IO ]; then
                debug 3 " skip non-I/O subchannel"
                continue
            fi
	fi
        # get subchannel information...
        # ATTENTION: hex values from sysfs are WITHOUT leading 0x prefix!
        read chpid_list < $subch_p/chpids
        read -a chpids <<< "$chpid_list"
        if [ ${#chpids[@]} -ne 8 ]; then
            error $"sysfs reported ${#chpids[@]} CHPIDs instead of expected 8"
        fi
        read pim pam pom foo < $subch_p/pimpampom
        pimchpidZ=""
        local chp
        for ((chp=0; chp < 8; chp++)); do
            mask=$((0x80 >> chp))
            if (( 0x$pim & $mask )); then
                pimchpidZ=${pimchpidZ}${chpids[chp]}
            else
                pimchpidZ=${pimchpidZ}"ZZ"
            fi
        done
        # get device information...
        read cutype < $dev_p/cutype
        read active < $dev_p/online
        # skip already active subchannels and those that are already in a
        # ccwgroup and thus not available any more:
        [ $active == "1" ] && continue
        [ -h $dev_p/group_device ] && continue
        # get chpid information...
        pimchpids=${pimchpidZ//ZZ/}
        [ $pimchpids == "" ] && continue
        # Taking the first 2 hex digits as CHPID relies somewhat on the fact
        # that network adaptors don't use multipathing and only have one CHP.
        # Anyway it's OK since we're only interested in CHPID type and I guess
        # this should be equal for all possible multipaths to the same device.
        chpid=${pimchpids:0:2}
        chpid_p=css$prefix/chp$prefix.$chpid
        read chptype < $chpid_p/type
        # filter and output...
        if search_cu $cutype; then
            if [ "${CU_DEVDRV[$cu_idx]}" == "ctc" ]; then
                # assume CTC are mostly virtual and ignore chpid from sysfs
                chpidtype_symbolic="-"
            else
                search_chpt $chptype
            fi
            echo $pimchpids $devbusid $STARTPATH/$dev_p $cutype $chpidtype_symbolic $chptype ${CU_DEVDRV[$cu_idx]} ${CU_DEVNAME[$cu_idx]} ${CU_GROUPCHANNELS[$cu_idx]} $cu_idx ${CU_CARDTYPE[$cu_idx]}
        else
            debug 5 " skip non-network device $devbusid CU $cutype"
        fi
    done
}

# search_groups:
#
# Prints enumeration list on standard output consisting of possible
# hardware configurations (ccwgroups) for network devices on s390.
# Each configuration suggestion includes corresponding attributes
# that are of potential interest for the user and fit in a fixed column
# table on an 80 column screen.
#
# PRECONDITION: Standard input has to be stably sorted by device bus IDs and
# then by CHPIDs, i.e. grouped by CHPIDs.
#
function search_groups() {
    local w_prefix w_ssid w_devno
    local d_prefix d_ssid d_devno
    local prefix ssid devno x
    local chp devbusid dev_p cutype chpidtypename chptype devdrv devname groupchs cu_idx cardtype
    # remembered last state variables for possible ccwgroup:
    local r_prefix="Z"
    local r_ssid="Z"
    local r_devno="ZZZZ"
    local r_chp="ZZ"
    local r_cutype="ZZZZ/ZZ"
    local count=0
    local item=1
    local skipped=0
    # currently unused are: dev_p,chptype, cu_idx.
    while read chp devbusid dev_p cutype chpidtypename chptype devdrv devname groupchs cu_idx cardtype; do
        debug 1 " # $chp $devbusid $dev_p $cutype $chpidtypename $chptype $devdrv $devname $groupchs $cu_idx $cardtype"
        IFS=.
        read prefix ssid devno x <<< "$devbusid"
        unset IFS
        if [ $r_chp != $chp \
            -o $r_prefix != $prefix \
            -o $r_ssid != $ssid \
            -o $r_cutype != $cutype ]; then
            # restart with new read channel info and remember it
            r_prefix=$prefix
            r_ssid=$ssid
            r_devno=$devno
            r_chp=$chp
            r_cutype=$cutype
            count=1
            debug 2 " INFO: restart on different CHPID or prefix or CUtype/model"
            continue
        fi
        count=$((count + 1))
        if [ $count -eq 2 ]; then
            # about to check if write channel is one above read channel
            if [ $((0x$devno)) -ne $((0x$r_devno + 1)) ]; then
                # start with new read channel info
                r_prefix=$prefix
                r_ssid=$ssid
                r_devno=$devno
                r_chp=$chp
                r_cutype=$cutype
                count=1
                skipped=$((skipped + 1))
                # unimplemented possible packed channel usage option:
                # remember unused channels for later use as data channel
                debug 2 " INFO: restart on unmatching read channel"
                continue
            fi
            w_prefix=$prefix
            w_ssid=$ssid
            w_devno=$devno
        elif [ $count -eq 3 ]; then
            # remember data channel info
            d_prefix=$prefix
            d_ssid=$ssid
            d_devno=$devno
        fi
        debug 2 " INFO: groupchs=$groupchs count=$count"
        if [ $count -ne $groupchs ]; then
            debug 2 " INFO: skip"
            continue
        fi
        # found possible ccwgroup
        case $count in
            2)
                chlist=$r_prefix.$r_ssid.$r_devno,$w_prefix.$w_ssid.$w_devno
                ;;
            3)
                chlist=$r_prefix.$r_ssid.$r_devno,$w_prefix.$w_ssid.$w_devno,$d_prefix.$d_ssid.$d_devno
                ;;
            *)
                error $"unknown number of channels for group, code needs to be fixed"
                ;;
        esac
        echo $item $cutype $chp $chpidtypename $devdrv $devname $chlist "$cardtype"
        item=$((item + 1))
        # restart after successful detection
        r_prefix="Z"
        count=0
    done
    debug 1 " STATISTIC: skipped $skipped devnos because of unmatching read channel"
}

build_list |
# stable sort by device bus IDs and then by CHPIDs => grouped by CHPIDs
# (sorting only works since keys are fixed no. of digits with leading zeros!)
sort -s -k 1,1 -k 2,2 |
#cat ; exit # move at desired line and uncomment to see intermediate output
search_groups