summaryrefslogtreecommitdiffstats
path: root/modules.d/95iscsi/module-setup.sh
blob: d3307b98b82811c217c2af572af90b0be1fdac9b (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
#!/bin/bash

# called by dracut
check() {
    # If our prerequisites are not met, fail anyways.
    require_binaries iscsi-iname iscsiadm iscsid || return 1
    require_kernel_modules iscsi_tcp || return 1

    # If hostonly was requested, fail the check if we are not actually
    # booting from root.
    [[ $hostonly ]] || [[ $mount_needs ]] && {
        pushd . > /dev/null
        for_each_host_dev_and_slaves block_is_iscsi
        local _is_iscsi=$?
        popd > /dev/null || exit
        [[ $_is_iscsi == 0 ]] || return 255
    }
    return 0
}

get_ibft_mod() {
    local ibft_mac=$1
    local iface_mac iface_mod
    # Return the iSCSI offload module for a given MAC address
    for iface_desc in $(iscsiadm -m iface | cut -f 2 -d ' '); do
        iface_mod=${iface_desc%%,*}
        iface_mac=${iface_desc#*,}
        iface_mac=${iface_mac%%,*}
        if [ "$ibft_mac" = "$iface_mac" ]; then
            echo "$iface_mod"
            return 0
        fi
    done
}

install_ibft() {
    # When iBFT / iscsi_boot is detected:
    # - Use 'ip=ibft' to set up iBFT network interface
    #   Note: bnx2i is using a different MAC address of iSCSI offloading
    #         so the 'ip=ibft' parameter must not be set
    # - specify firmware booting cmdline parameter

    for d in /sys/firmware/*; do
        if [ -d "${d}"/ethernet0 ]; then
            read -r ibft_mac < "${d}"/ethernet0/mac
            ibft_mod=$(get_ibft_mod "$ibft_mac")
        fi
        if [ -z "$ibft_mod" ] && [ -d "${d}"/ethernet1 ]; then
            read -r ibft_mac < "${d}"/ethernet1/mac
            ibft_mod=$(get_ibft_mod "$ibft_mac")
        fi
        if [ -d "${d}"/initiator ]; then
            if [ "${d##*/}" = "ibft" ] && [ "$ibft_mod" != "bnx2i" ]; then
                echo -n "rd.iscsi.ibft=1 "
            fi
            echo -n "rd.iscsi.firmware=1 "
        fi
    done
}

install_iscsiroot() {
    local devpath=$1
    local scsi_path iscsi_lun session c d conn host flash
    local iscsi_session iscsi_address iscsi_port iscsi_targetname

    scsi_path=${devpath%%/block*}
    [ "$scsi_path" = "$devpath" ] && return 1
    iscsi_lun=${scsi_path##*:}
    [ "$iscsi_lun" = "$scsi_path" ] && return 1
    session=${devpath%%/target*}
    [ "$session" = "$devpath" ] && return 1
    iscsi_session=${session##*/}
    [ "$iscsi_session" = "$session" ] && return 1
    host=${session%%/session*}
    [ "$host" = "$session" ] && return 1
    iscsi_host=${host##*/}

    for flash in "${host}"/flashnode_sess-*; do
        [ -f "$flash" ] || continue
        [ ! -e "$flash/is_boot_target" ] && continue
        read -r is_boot < "$flash"/is_boot_target
        if [ "$is_boot" -eq 1 ]; then
            # qla4xxx flashnode session; skip iBFT discovery
            read -r iscsi_initiator < /sys/class/iscsi_host/"${iscsi_host}"/initiatorname
            echo "rd.iscsi.initiator=${iscsi_initiator}"
            return
        fi
    done

    for d in "${session}"/*; do
        case $d in
            *connection*)
                c=${d##*/}
                conn=${d}/iscsi_connection/${c}
                if [ -d "${conn}" ]; then
                    read -r iscsi_address < "${conn}"/persistent_address
                    read -r iscsi_port < "${conn}"/persistent_port
                fi
                ;;
            *session)
                if [ -d "${d}"/"${iscsi_session}" ]; then
                    read -r iscsi_initiator < "${d}"/"${iscsi_session}"/initiatorname
                    read -r iscsi_targetname < "${d}"/"${iscsi_session}"/targetname
                fi
                ;;
        esac
    done

    [ -z "$iscsi_address" ] && return
    ip_params_for_remote_addr "$iscsi_address"

    if [ -n "$iscsi_address" ] && [ -n "$iscsi_targetname" ]; then
        if [ -n "$iscsi_port" ] && [ "$iscsi_port" -eq 3260 ]; then
            iscsi_port=
        fi
        if [ -n "$iscsi_lun" ] && [ "$iscsi_lun" -eq 0 ]; then
            iscsi_lun=
        fi
        # In IPv6 case rd.iscsi.initatior= must pass address in [] brackets
        case "$iscsi_address" in
            *:*)
                iscsi_address="[$iscsi_address]"
                ;;
        esac
        # Must be two separate lines, so that "sort | uniq" commands later
        # can sort out rd.iscsi.initiator= duplicates
        echo "rd.iscsi.initiator=${iscsi_initiator}"
        echo "netroot=iscsi:${iscsi_address}::${iscsi_port}:${iscsi_lun}:${iscsi_targetname}"
        echo "rd.neednet=1"
    fi
    return 0
}

install_softiscsi() {
    [ -d /sys/firmware/ibft ] && return 0

    # shellcheck disable=SC2317  # called later by for_each_host_dev_and_slaves
    is_softiscsi() {
        local _dev=$1
        local iscsi_dev

        [[ -L "/sys/dev/block/$_dev" ]] || return
        iscsi_dev=$(
            cd -P /sys/dev/block/"$_dev" || exit
            echo "$PWD"
        )
        install_iscsiroot "$iscsi_dev"
    }

    for_each_host_dev_and_slaves_all is_softiscsi || return 255
    return 0
}

# called by dracut
depends() {
    echo network rootfs-block
}

# called by dracut
installkernel() {
    local _arch=${DRACUT_ARCH:-$(uname -m)}
    local _funcs='iscsi_register_transport'

    instmods bnx2i qla4xxx cxgb3i cxgb4i be2iscsi qedi
    hostonly="" instmods iscsi_tcp iscsi_ibft crc32c iscsi_boot_sysfs 8021q

    if [ "$_arch" = "s390" ] || [ "$_arch" = "s390x" ]; then
        _s390drivers="=drivers/s390/scsi"
    fi

    dracut_instmods -o -s ${_funcs} =drivers/scsi ${_s390drivers:+"$_s390drivers"}
}

# called by dracut
cmdline() {
    local _iscsiconf
    _iscsiconf=$(install_ibft)
    {
        if [ "$_iscsiconf" ]; then
            echo "${_iscsiconf}"
        else
            install_softiscsi
        fi
    } | sort | uniq
}

# called by dracut
install() {
    inst_multiple -o iscsiuio
    inst_libdir_file 'libgcc_s.so*'
    inst_multiple umount iscsi-iname iscsiadm iscsid
    inst_binary sort

    inst_multiple -o \
        "$systemdsystemunitdir"/iscsid.socket \
        "$systemdsystemunitdir"/iscsid.service \
        "$systemdsystemunitdir"/iscsiuio.service \
        "$systemdsystemunitdir"/iscsiuio.socket \
        "$systemdsystemunitdir"/sockets.target.wants/iscsid.socket \
        "$systemdsystemunitdir"/sockets.target.wants/iscsiuio.socket

    inst_simple /etc/iscsi/iscsid.conf
    if [[ $hostonly ]]; then
        inst_simple /etc/iscsi/initiatorname.iscsi
    fi

    # Detect iBFT and perform mandatory steps
    if [[ $hostonly_cmdline == "yes" ]]; then
        local _iscsiconf
        _iscsiconf=$(cmdline)
        [[ $_iscsiconf ]] && printf "%s\n" "$_iscsiconf" >> "${initdir}/etc/cmdline.d/95iscsi.conf"
    fi

    inst_hook cmdline 90 "$moddir/parse-iscsiroot.sh"
    inst_hook cleanup 90 "$moddir/cleanup-iscsi.sh"
    inst "$moddir/iscsiroot.sh" "/sbin/iscsiroot"

    if ! dracut_module_included "systemd"; then
        inst "$moddir/mount-lun.sh" "/bin/mount-lun.sh"
    else
        inst_multiple -o \
            "$systemdsystemunitdir"/iscsi.service \
            "$systemdsystemunitdir"/iscsi-init.service \
            "$systemdsystemunitdir"/iscsid.service \
            "$systemdsystemunitdir"/iscsid.socket \
            "$systemdsystemunitdir"/iscsiuio.service \
            "$systemdsystemunitdir"/iscsiuio.socket \
            iscsiadm iscsid

        for i in \
            iscsid.socket \
            iscsiuio.socket; do
            $SYSTEMCTL -q --root "$initdir" enable "$i"
        done

        mkdir -p "${initdir}/$systemdsystemunitdir/iscsid.service.d"
        {
            echo "[Unit]"
            echo "DefaultDependencies=no"
            echo "Conflicts=shutdown.target"
            echo "Before=shutdown.target"
        } > "${initdir}/$systemdsystemunitdir/iscsid.service.d/dracut.conf"

        mkdir -p "${initdir}/$systemdsystemunitdir/iscsid.socket.d"
        {
            echo "[Unit]"
            echo "DefaultDependencies=no"
            echo "Conflicts=shutdown.target"
            echo "Before=shutdown.target sockets.target"
        } > "${initdir}/$systemdsystemunitdir/iscsid.socket.d/dracut.conf"

        mkdir -p "${initdir}/$systemdsystemunitdir/iscsiuio.service.d"
        {
            echo "[Unit]"
            echo "DefaultDependencies=no"
            echo "Conflicts=shutdown.target"
            echo "Before=shutdown.target"
        } > "${initdir}/$systemdsystemunitdir/iscsiuio.service.d/dracut.conf"

        mkdir -p "${initdir}/$systemdsystemunitdir/iscsiuio.socket.d"
        {
            echo "[Unit]"
            echo "DefaultDependencies=no"
            echo "Conflicts=shutdown.target"
            echo "Before=shutdown.target sockets.target"
        } > "${initdir}/$systemdsystemunitdir/iscsiuio.socket.d/dracut.conf"

        # Fedora 34 iscsid requires iscsi-shutdown.service
        # which would terminate all iSCSI connections on switch root
        cat > "${initdir}/$systemdsystemunitdir/iscsi-shutdown.service" << EOF
[Unit]
Description=Dummy iscsi-shutdown.service for the initrd
Documentation=man:iscsid(8) man:iscsiadm(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-remount-fs.service network.target iscsid.service iscsiuio.service
Before=remote-fs-pre.target

[Service]
Type=oneshot
RemainAfterExit=false
ExecStart=-/usr/bin/true
EOF
    fi
    inst_dir /var/lib/iscsi
    mkdir -p "${initdir}/var/lib/iscsi/nodes"
    # Fedora 34 iscsid wants a non-empty /var/lib/iscsi/nodes directory
    : > "${initdir}/var/lib/iscsi/nodes/.dracut"
    dracut_need_initqueue
}