O Debian Wiki tem uma entrada excelente descrevendo o que eu precisava. Depois disso, criei minhas próprias regras em /etc/udev/rules.d/20-disk-bay.rules. Eu incluí apenas os dois primeiros mapeamentos de porta sata como exemplo:
# There are different DEVPATHs for major kernel versions!
# Example for SATA N:
#
# Kernel < 3 DEVPATH
# *1f.2/hostN/targetN:0:0/N:0:0:0*
#
# Kernel > 3 DEVPATH
# *1f.2/ata(N+1)/host*
########## Map SATA 0 to /dev/sdb ##############
# Kernel < 3
KERNEL=="sd?", SUBSYSTEM=="block", DEVPATH=="*1f.2/host0/target0:0:0/0:0:0:0*", NAME="sdb", RUN+="/usr/bin/logger My disk ATTR{partition}=$ATTR{partition}, DEVPATH=$devpath, ID_PATH=$ENV{ID_PATH}, ID_SERIAL=$ENV{ID_SERIAL}", GOTO="END_20_PERSISTENT_DISK"
KERNEL=="sd?*", ATTR{partition}=="1", SUBSYSTEM=="block", DEVPATH=="*1f.2/host0/target0:0:0/0:0:0:0*", NAME="sdb%n", RUN+="/usr/bin/logger My partition parent=%p number=%n, ATTR{partition}=$ATTR{partition}"
# Kernel > 3
KERNEL=="sd?", SUBSYSTEM=="block", DEVPATH=="*1f.2/ata1/host*", NAME="sdb", RUN+="/usr/bin/logger My disk ATTR{partition}=$ATTR{partition}, DEVPATH=$devpath, ID_PATH=$ENV{ID_PATH}, ID_SERIAL=$ENV{ID_SERIAL}", GOTO="END_20_PERSISTENT_DISK"
KERNEL=="sd?*", ATTR{partition}=="1", SUBSYSTEM=="block", DEVPATH=="*1f.2/ata1/host*", NAME="sdb%n", RUN+="/usr/bin/logger My partition parent=%p number=%n, ATTR{partition}=$ATTR{partition}"
########## Map SATA 1 to /dev/sdc ##############
# Kernel < 3
KERNEL=="sd?", SUBSYSTEM=="block", DEVPATH=="*1f.2/host1/target1:0:0/1:0:0:0*", NAME="sdc", RUN+="/usr/bin/logger My disk ATTR{partition}=$ATTR{partition}, DEVPATH=$devpath, ID_PATH=$ENV{ID_PATH}, ID_SERIAL=$ENV{ID_SERIAL}", GOTO="END_20_PERSISTENT_DISK"
KERNEL=="sd?*", ENV{DEVTYPE}=="partition", SUBSYSTEM=="block", DEVPATH=="*1f.2/host1/target1:0:0/1:0:0:0*", NAME="sdc%n", RUN+="/usr/bin/logger My partition parent=%p number=%n, ATTR{partition}=$ATTR{partition}"
# Kernel > 3
KERNEL=="sd?", SUBSYSTEM=="block", DEVPATH=="*1f.2/ata2/host*", NAME="sdc", RUN+="/usr/bin/logger My disk ATTR{partition}=$ATTR{partition}, DEVPATH=$devpath, ID_PATH=$ENV{ID_PATH}, ID_SERIAL=$ENV{ID_SERIAL}", GOTO="END_20_PERSISTENT_DISK"
KERNEL=="sd?*", ATTR{partition}=="1", SUBSYSTEM=="block", DEVPATH=="*1f.2/ata2/host*", NAME="sdc%n", RUN+="/usr/bin/logger My partition parent=%p number=%n, ATTR{partition}=$ATTR{partition}"
LABEL="END_20_PERSISTENT_DISK"
As regras acima sempre mapearão qualquer unidade colocada na porta SATA 0, a primeira porta SATA física na minha placa-mãe, como / dev / sdb e qualquer unidade colocada no SATA 1 como / dev / sdc Os mapeamentos consistentes de portas físicas são críticos no meu caso de uso, pois tenho 5 arrays RAID-1 nos quais os discos podem ser arbitrariamente trocados de seus compartimentos de hotswap físico. Um usuário não técnico pode trocar esses discos a qualquer momento sem ter que lidar com IDs de dispositivos - o sistema é totalmente autônomo e não construirá os arrays RAID nos discos errados nos compartimentos de hotswap. Este é um caso de uso muito específico, mas espero que ajude alguém no futuro.