/btrfs-rescrub-errors
Last active Dec 4, 2019
btrfs-rescrub-errors
| #!/bin/bash | |
| # Scan dmesg for btrfs error messages, and re-scrub the respective | |
| # offsets. Needs btrfs scrub with -s / -e switches: | |
| # https://patchwork.kernel.org/patch/11268681/ | |
| set -eEuo pipefail | |
| shopt -s lastpipe | |
| blocksize=$((1*1024*1024*1024)) | |
| declare -A log2phys_cache | |
| function log2phys() { | |
| local dev=$1 | |
| local logaddr=$2 | |
| log2phys_reply=${log2phys_cache[$logaddr $dev]-} | |
| if [[ -z "$log2phys_reply" ]] | |
| then | |
| log2phys_reply=$(btrfs-map-logical "$dev" -l "$logaddr" 2>/dev/null) | |
| log2phys_cache[$logaddr $dev]=$log2phys_reply | |
| fi | |
| } | |
| declare -A bad | |
| printf 'Parsing dmesg...\n' 1>&2 | |
| dmesg | | |
| while read -r line | |
| do | |
| if [[ "$line" =~ \ error\ at\ logical\ [^\ ]*\ on\ dev\ (.*),\ physical\ ([^ ]*), ]] | |
| then | |
| dev=${BASH_REMATCH[1]} | |
| physaddr=${BASH_REMATCH[2]} | |
| printf '%s %s\n' "$physaddr" "$dev" | |
| elif [[ "$line" =~ \ error\ at\ logical\ ([^ ]*)\ on\ dev\ ([^ ]*)$ ]] | |
| then | |
| logaddr=${BASH_REMATCH[1]} | |
| dev=${BASH_REMATCH[2]} | |
| log2phys "$dev" "$logaddr" | |
| printf %s "$log2phys_reply" | \ | |
| while read -r line | |
| do | |
| if [[ "$line" =~ (^mirror [^ ]* logical [^ ]* physical ([^ ]*) device (.*)$) ]] | |
| then | |
| physaddr=${BASH_REMATCH[2]} | |
| dev=${BASH_REMATCH[3]} | |
| else | |
| printf 'Failed to parse btrfs-map-logical output: %q\n' "$line" 1>&2 | |
| continue | |
| fi | |
| done | |
| printf '%s %s\n' "$physaddr" "$dev" | |
| fi | |
| done | | |
| while read -r addr dev | |
| do | |
| block=$((addr/blocksize)) | |
| bad[$((block-1)) $dev]=1 | |
| bad[$((block )) $dev]=1 | |
| bad[$((block+1)) $dev]=1 | |
| done | |
| printf '%s\n' "${!bad[@]}" | | |
| sort -n | | |
| while read -r block dev | |
| do | |
| if [[ ! -v "bad[$block $dev]" ]] | |
| then | |
| continue | |
| fi | |
| unset 'bad[$block $dev]' | |
| block2=$block | |
| while [[ -v "bad[$((block2+1)) $dev]" ]] | |
| do | |
| block2=$((block2+1)) | |
| unset 'bad[$block2 $dev]' | |
| done | |
| addr1=$((block *blocksize)) | |
| addr2=$((block2*blocksize)) | |
| printf 'Rescanning %s - %s on %s:\n' "$addr1" "$addr2" "$dev" 1>&2 | |
| btrfs scrub start -B -s "$addr1" -e "$addr2" "$dev" | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment