Showing 4 changed files with 85 additions and 84 deletions
+74 -65
projects/thunderbolts/deploy/attempt1/common/sbin/tb-recover.sh
@@ -3,7 +3,6 @@ set -euo pipefail
3 3
 
4 4
 BRIDGE="thunderbridge"
5 5
 MTU="65520"
6
-FOUND_TB_IFACE=0
7 6
 STATE_DIR="/run/tb-recover"
8 7
 LAST_BOLT_RESTART_FILE="${STATE_DIR}/last_bolt_restart_epoch"
9 8
 BOLT_RESTART_COOLDOWN_SEC=600
@@ -14,9 +13,11 @@ PEER_FAIL_THRESHOLD="${TB_PEER_FAIL_THRESHOLD:-2}"
14 13
 IFACE_CYCLE_COOLDOWN_SEC="${TB_IFACE_CYCLE_COOLDOWN_SEC:-300}"
15 14
 IFACE_CYCLE_SETTLE_SEC="${TB_IFACE_CYCLE_SETTLE_SEC:-5}"
16 15
 PING_TIMEOUT_SEC="${TB_PING_TIMEOUT_SEC:-1}"
16
+MONITOR_INTERVAL_SEC="${TB_MONITOR_INTERVAL_SEC:-60}"
17 17
 LOCAL_HOST="$(hostname -s 2>/dev/null || hostname)"
18 18
 
19 19
 mkdir -p "$STATE_DIR"
20
+trap "log 'Shutting down tb-recover'; exit 0" SIGTERM SIGINT
20 21
 
21 22
 log() {
22 23
   printf '%s %s\n' "$(date -Is)" "$*"
@@ -58,6 +59,23 @@ read_counter_file() {
58 59
 peer_ip_for_iface() {
59 60
   local iface="$1"
60 61
 
62
+  # Dynamically resolve peer by looking up the XDomain device name bound to this
63
+  # interface.  The kernel exposes a symlink at /sys/class/net/<iface>/device
64
+  # pointing to the XDomain service path (e.g. .../1-1.0).  Its parent directory
65
+  # is the XDomain device whose device_name attribute holds the peer hostname.
66
+  local dev_path xdomain_dev peer_name
67
+  dev_path="$(readlink -f "/sys/class/net/${iface}/device" 2>/dev/null || true)"
68
+  if [ -n "$dev_path" ] && [ -d "$dev_path" ]; then
69
+    xdomain_dev="$(dirname "$dev_path")"
70
+    peer_name="$(cat "${xdomain_dev}/device_name" 2>/dev/null || true)"
71
+    case "$peer_name" in
72
+      baobab) printf '%s\n' "192.168.10.91" ; return 0 ;;
73
+      ebony)  printf '%s\n' "192.168.10.92" ; return 0 ;;
74
+      tapia)  printf '%s\n' "192.168.10.93" ; return 0 ;;
75
+    esac
76
+  fi
77
+
78
+  # Static fallback (used when sysfs path is not available).
61 79
   case "${LOCAL_HOST}:${iface}" in
62 80
     baobab:thunderbolt0)
63 81
       printf '%s\n' "192.168.10.92"
@@ -234,83 +252,74 @@ run_nhi_rescan() {
234 252
   return 1
235 253
 }
236 254
 
237
-# Keep the bridge present and up before trying to enslave ports.
238
-ip link show "$BRIDGE" >/dev/null 2>&1 || ip link add name "$BRIDGE" type bridge || true
239
-ip link set "$BRIDGE" mtu "$MTU" || true
240
-ip link set "$BRIDGE" up || true
241
-
242
-for path in /sys/class/net/thunderbolt*; do
243
-  [ -e "$path" ] || continue
244
-  IFACE="${path##*/}"
245
-  FOUND_TB_IFACE=1
246
-  ip link set "$IFACE" up || true
247
-  ip link set "$IFACE" mtu "$MTU" || true
248
-  ip link set "$IFACE" master "$BRIDGE" || true
249
-  systemctl start "tb-enlist@${IFACE}.service" || true
250
-done
251
-
252
-# If no thunderbolt netdev exists but a TB domain exists, force a rescan + udev retrigger.
253
-if [ "$FOUND_TB_IFACE" -eq 0 ] && [ -d /sys/bus/thunderbolt/devices ]; then
254
-  trigger_tb_rescan
255
-
256
-  # Escalate with cooldown: try PCI NHI remove+rescan to emulate a soft replug.
257
-  sleep 2
258
-  if ! has_tb_netdev; then
259
-    now="$(date +%s)"
260
-    last="0"
261
-    if [ -f "$LAST_BOLT_RESTART_FILE" ]; then
262
-      last="$(cat "$LAST_BOLT_RESTART_FILE" 2>/dev/null || echo 0)"
263
-    fi
255
+init_bridge() {
256
+  ip link show "$BRIDGE" >/dev/null 2>&1 || ip link add name "$BRIDGE" type bridge || true
257
+  ip link set "$BRIDGE" mtu "$MTU" || true
258
+  ip link set "$BRIDGE" up || true
259
+}
264 260
 
265
-    case "$last" in
266
-      ''|*[!0-9]*)
267
-        last=0
268
-        ;;
269
-    esac
261
+handle_missing_interfaces() {
262
+  local found_tb_iface=0
263
+
264
+  for path in /sys/class/net/thunderbolt*; do
265
+    [ -e "$path" ] || continue
266
+    found_tb_iface=1
267
+    IFACE="${path##*/}"
268
+    ip link set "$IFACE" up || true
269
+    ip link set "$IFACE" mtu "$MTU" || true
270
+    ip link set "$IFACE" master "$BRIDGE" || true
271
+    systemctl start "tb-enlist@${IFACE}.service" || true
272
+  done
270 273
 
271
-    nhi_last="0"
272
-    if [ -f "$LAST_NHI_RESCAN_FILE" ]; then
273
-      nhi_last="$(cat "$LAST_NHI_RESCAN_FILE" 2>/dev/null || echo 0)"
274
-    fi
275
-    case "$nhi_last" in
276
-      ''|*[!0-9]*)
277
-        nhi_last=0
278
-        ;;
279
-    esac
274
+  if [ "$found_tb_iface" -eq 0 ] && [ -d /sys/bus/thunderbolt/devices ]; then
275
+    trigger_tb_rescan
276
+    sleep 2
280 277
 
281
-    if [ $((now - nhi_last)) -ge "$NHI_RESCAN_COOLDOWN_SEC" ]; then
282
-      if run_nhi_rescan "$now"; then
283
-        sleep "$NHI_SETTLE_SEC"
284
-        trigger_tb_rescan
285
-
286
-        # On newer kernels the first NHI reset can stop at the peer xdomain host
287
-        # node without recreating the matching *.0 network service.
288
-        if ! has_tb_netdev && has_stale_tb_xdomain; then
289
-          retry_now="$(date +%s)"
290
-          if run_nhi_rescan "$retry_now"; then
291
-            sleep "$NHI_SETTLE_SEC"
292
-            trigger_tb_rescan
278
+    if ! has_tb_netdev; then
279
+      now="$(date +%s)"
280
+      last="$(read_epoch_file "$LAST_BOLT_RESTART_FILE")"
281
+      nhi_last="$(read_epoch_file "$LAST_NHI_RESCAN_FILE")"
282
+
283
+      if [ $((now - nhi_last)) -ge "$NHI_RESCAN_COOLDOWN_SEC" ]; then
284
+        if run_nhi_rescan "$now"; then
285
+          sleep "$NHI_SETTLE_SEC"
286
+          trigger_tb_rescan
287
+
288
+          if ! has_tb_netdev && has_stale_tb_xdomain; then
289
+            retry_now="$(date +%s)"
290
+            if run_nhi_rescan "$retry_now"; then
291
+              sleep "$NHI_SETTLE_SEC"
292
+              trigger_tb_rescan
293
+            fi
293 294
           fi
294 295
         fi
295 296
       fi
296
-    fi
297 297
 
298
-    # Secondary fallback with cooldown: restart boltd if interface is still missing
299
-    # and the host actually uses that service.
300
-    if ! has_tb_netdev; then
301
-      if [ $((now - last)) -ge "$BOLT_RESTART_COOLDOWN_SEC" ]; then
298
+      if ! has_tb_netdev && [ $((now - last)) -ge "$BOLT_RESTART_COOLDOWN_SEC" ]; then
302 299
         if systemctl list-unit-files bolt.service >/dev/null 2>&1; then
303 300
           systemctl restart bolt.service || true
304 301
           printf '%s\n' "$now" > "$LAST_BOLT_RESTART_FILE"
305 302
         fi
306 303
       fi
307
-    fi
308 304
 
309
-    trigger_tb_rescan
305
+      trigger_tb_rescan
306
+    fi
310 307
   fi
311
-fi
308
+}
309
+
310
+monitor_interfaces() {
311
+  for path in /sys/class/net/thunderbolt*; do
312
+    [ -e "$path" ] || continue
313
+    assess_peer_health "${path##*/}"
314
+  done
315
+}
316
+
317
+init_bridge
318
+
319
+log "tb-recover monitor started (interval: ${MONITOR_INTERVAL_SEC}s)"
312 320
 
313
-for path in /sys/class/net/thunderbolt*; do
314
-  [ -e "$path" ] || continue
315
-  assess_peer_health "${path##*/}"
321
+while true; do
322
+  handle_missing_interfaces
323
+  monitor_interfaces
324
+  sleep "$MONITOR_INTERVAL_SEC"
316 325
 done
+6 -1
projects/thunderbolts/deploy/attempt1/common/systemd/system/tb-recover.service
@@ -4,5 +4,10 @@ After=tb-bridge.service bolt.service
4 4
 Wants=tb-bridge.service
5 5
 
6 6
 [Service]
7
-Type=oneshot
7
+Type=simple
8
+Restart=on-failure
9
+RestartSec=10
8 10
 ExecStart=/usr/local/sbin/tb-recover.sh
11
+
12
+[Install]
13
+WantedBy=multi-user.target
+0 -11
projects/thunderbolts/deploy/attempt1/common/systemd/system/tb-recover.timer
@@ -1,11 +0,0 @@
1
-[Unit]
2
-Description=Periodic Thunderbolt recovery probe
3
-
4
-[Timer]
5
-OnBootSec=30s
6
-OnUnitActiveSec=30s
7
-AccuracySec=5s
8
-Unit=tb-recover.service
9
-
10
-[Install]
11
-WantedBy=timers.target
+5 -7
projects/thunderbolts/deploy/attempt1/deploy_tb.sh
@@ -35,7 +35,6 @@ COMMON_UDEV="$BASE_DIR/common/udev/rules.d/90-thunderbolt-net-systemd.rules"
35 35
 COMMON_SVC1="$BASE_DIR/common/systemd/system/tb-enlist@.service"
36 36
 COMMON_SVC2="$BASE_DIR/common/systemd/system/tb-bridge.service"
37 37
 COMMON_SVC3="$BASE_DIR/common/systemd/system/tb-recover.service"
38
-COMMON_TMR1="$BASE_DIR/common/systemd/system/tb-recover.timer"
39 38
 COMMON_BIN1="$BASE_DIR/common/sbin/tb-recover.sh"
40 39
 
41 40
 require() {
@@ -80,7 +79,6 @@ deploy_node() {
80 79
   scp -q "$COMMON_SVC1" "${SSH_USER}@${ip}:/etc/systemd/system/tb-enlist@.service"
81 80
   scp -q "$COMMON_SVC2" "${SSH_USER}@${ip}:/etc/systemd/system/tb-bridge.service"
82 81
   scp -q "$COMMON_SVC3" "${SSH_USER}@${ip}:/etc/systemd/system/tb-recover.service"
83
-  scp -q "$COMMON_TMR1" "${SSH_USER}@${ip}:/etc/systemd/system/tb-recover.timer"
84 82
   scp -q "$COMMON_BIN1" "${SSH_USER}@${ip}:/usr/local/sbin/tb-recover.sh"
85 83
 
86 84
   echo "==> [$host@$ip] copy NODE config"
@@ -95,14 +93,14 @@ chmod 0644 /etc/udev/rules.d/90-thunderbolt-net-systemd.rules
95 93
 chmod 0644 /etc/systemd/system/tb-enlist@.service
96 94
 chmod 0644 /etc/systemd/system/tb-bridge.service
97 95
 chmod 0644 /etc/systemd/system/tb-recover.service
98
-chmod 0644 /etc/systemd/system/tb-recover.timer
99 96
 chmod 0755 /usr/local/sbin/tb-recover.sh
100 97
 systemctl daemon-reload
101 98
 udevadm control --reload
102 99
 command -v ifreload >/dev/null 2>&1 && ifreload -a || true
103 100
 systemctl enable --now tb-bridge.service
104
-systemctl enable --now tb-recover.timer
105
-systemctl start tb-recover.service
101
+systemctl enable --now tb-recover.service
102
+rm -f /etc/systemd/system/tb-recover.timer
103
+systemctl daemon-reload
106 104
 udevadm trigger --subsystem-match=net --action=add
107 105
 EOF
108 106
 
@@ -110,7 +108,7 @@ EOF
110 108
   ssh $SSH_OPTS "${SSH_USER}@${ip}" bash -s <<'EOF'
111 109
 set -e
112 110
 systemctl --no-pager --plain --full status tb-bridge.service | sed -n '1,6p'
113
-systemctl --no-pager --plain --full status tb-recover.timer | sed -n '1,8p'
111
+systemctl --no-pager --plain --full status tb-recover.service | sed -n '1,8p'
114 112
 systemctl --no-pager --plain --full list-units 'tb-enlist@*.service' | sed -n '1,12p' || true
115 113
 ip -d link show thunderbridge | sed -n '1,3p'
116 114
 bridge link | grep -E 'thunderbolt|thunderbridge' || true
@@ -120,7 +118,7 @@ EOF
120 118
   echo
121 119
 }
122 120
 
123
-require "$COMMON_UDEV" "$COMMON_SVC1" "$COMMON_SVC2" "$COMMON_SVC3" "$COMMON_TMR1" "$COMMON_BIN1"
121
+require "$COMMON_UDEV" "$COMMON_SVC1" "$COMMON_SVC2" "$COMMON_SVC3" "$COMMON_BIN1"
124 122
 
125 123
 for h in "${TARGETS[@]}"; do
126 124
   deploy_node "$h"