Skip to content

Instantly share code, notes, and snippets.

@CyberShadow
Last active December 4, 2019 20:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CyberShadow/648a040103fb08738783b6435da376fe to your computer and use it in GitHub Desktop.
Save CyberShadow/648a040103fb08738783b6435da376fe to your computer and use it in GitHub Desktop.
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