Skip to main content
linbit.com linbit.com Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Emulating 512 Byte Sectors on 4K Native Backing Storage over iSCSI

This article describes how to present 512B sectors to iSCSI initiators when the underlying storage uses 4K native (4Kn) sectors. It covers manual targetcli and SCST examples, Pacemaker resource agent configuration, DRBD® Reactor promoter plugin configuration, and LINSTOR® Gateway examples.

Background

Some applications and operating systems expect 512B sector devices. When your backing storage uses 4Kn sectors (common with NVMe drives and certain SAN configurations), you might need to present 512B sectors to the iSCSI initiator.

💡 TIP: 512 byte emulation (512e) at the physical drive layer is always preferable to emulating the block size at the iSCSI target layer. Use the fileio approach in this article only as a last resort, for 4Kn drives that do not expose 512e.

A real-world example is Oracle Linux Virtualization Manager (OLVM) with LINSTOR and DRBD-backed iSCSI storage. OLVM (and its upstream project, oVirt) uses the sanlock daemon for storage domain locking. Because sanlock is not compatible with block sizes other than 512B, iSCSI and Fibre Channel storage domains must use legacy 512B blocks.

When deploying LINSTOR with DRBD replication on servers with 4Kn NVMe drives and presenting storage to OLVM over iSCSI, the iSCSI target must advertise 512B sectors for OLVM to use the storage domain.

The LIO and SCST iSCSI targets can advertise a different sector size to initiators than the backing device uses. However, their default block backstores cannot present a sector size smaller than the backing device, which is what emulating 512B on a 4Kn device requires.

📝 NOTE: iSCSI targets support two backstore types. The block backstore type issues direct I/O to the backing device with no caching layer. This type is known as iblock in targetcli, and vdisk_blockio in SCST. The other type, fileio, routes I/O through the kernel page cache, which can absorb misaligned writes and flush them in properly aligned chunks. This type is known as fileio in targetcli, and vdisk_fileio in SCST.

For HA configurations with LINSTOR and DRBD, use write-through mode so that data is flushed to disk before the iSCSI write is acknowledged.

IMPORTANT: The default block backstore natively accepts larger block sizes for initiators, for example, advertising a 512 native (512n) device as 4K. However, presenting a smaller block size, such as advertising a 4Kn device as 512B, requires a fileio backstore to align the I/O. See the testing results at the end of this article for details.

Prerequisites

This procedure requires the following:

  • Linux kernel with LIO target support (targetcli) or SCST installed
  • 4Kn physical storage
  • An iSCSI initiator on the client for testing:
    • RHEL: iscsi-initiator-utils
    • Debian and SUSE: open-iscsi

Identifying your backing device sector size

Run the following commands on the node hosting the backing storage:

blockdev --getpbsz /dev/nvme0n1  # physical sector size
blockdev --getss /dev/nvme0n1    # logical sector size

If both return 4096, the device is 4Kn.

Configuring iSCSI targets manually

The following instructions show how to manually configure iSCSI targets with a 512B block size when using targetcli or SCST.

targetcli with fileio backstore

Create a fileio backstore with write-through caching (write_back=false) and set block_size=512 before mapping the LUN:

targetcli /backstores/fileio create my_backstore /dev/nvme0n1 write_back=false

Set the block size to 512 by using configfs:

echo 512 > /sys/kernel/config/target/core/fileio_*/my_backstore/attrib/block_size

Create the iSCSI target and map the LUN:

targetcli /iscsi create iqn.2024-01.com.example:target1
targetcli /iscsi/iqn.2024-01.com.example:target1/tpg1/luns create /backstores/fileio/my_backstore 0
targetcli /iscsi/iqn.2024-01.com.example:target1/tpg1 set attribute authentication=0 demo_mode_write_protect=0 generate_node_acls=1 cache_dynamic_acls=1
targetcli /iscsi/iqn.2024-01.com.example:target1/tpg1/portals create 0.0.0.0

📝 NOTE: The block_size attribute must be set after backstore creation but before the LUN is mapped to the target. The kernel rejects changes after the LUN is active.

SCST with vdisk_fileio backstore

The SCST vdisk_fileio handler accepts the blocksize attribute at device creation time:

scstadmin -open_dev my_backstore -handler vdisk_fileio \
  -attributes "filename=/dev/nvme0n1,blocksize=512,write_through=1" -force -noprompt

Create the iSCSI target and map the LUN:

scstadmin -add_target iqn.2024-01.com.example:target1 -driver iscsi -force -noprompt
scstadmin -enable_target iqn.2024-01.com.example:target1 -driver iscsi -force -noprompt
scstadmin -add_lun 0 -driver iscsi \
  -target iqn.2024-01.com.example:target1 \
  -device my_backstore -force -noprompt

💡 TIP: When you manage the target with the iSCSILogicalUnit resource agent instead of configuring it by hand, you do not set these attributes directly. The agent exposes a single write_cache parameter (default 0, write-through) that it maps to the targetcli fileio write_back setting and to the SCST write_through setting at device creation time.

Verification

After configuring either targetcli or SCST, verify the block size from the iSCSI initiator.

Discover and log in to the target:

iscsiadm -m discovery -t sendtargets -p 192.168.1.100
iscsiadm -m node -T iqn.2024-01.com.example:target1 -p 192.168.1.100 --login

Verify the sector sizes on the iSCSI device:

blockdev --getpbsz /dev/sda  # physical sector size
blockdev --getss /dev/sda    # logical sector size

Both should return 512.

💡 TIP: These verification steps apply regardless of how the iSCSI target is managed. Use the same blockdev commands to verify block sizes when using Pacemaker, DRBD Reactor, or LINSTOR Gateway.

Installing the latest iSCSILogicalUnit resource agent

The block_size, liot_bstype, scst_bstype, and write_cache parameters require an updated iSCSILogicalUnit resource agent from the ClusterLabs resource agents repository. Until these changes are included in a release and shipped by distribution packages, you can install the updated resource agent manually from the ClusterLabs resource agents repository.

Determine the resource agent path for your distribution:

  • Most distributions: /lib/ocf/resource.d/heartbeat/
  • SUSE-based distributions: /usr/lib/ocf/resource.d/heartbeat/

Run the following script on all cluster nodes that run iSCSI targets:

# Set the path for your distribution
RA_PATH="/lib/ocf/resource.d/heartbeat"        # most distributions
# RA_PATH="/usr/lib/ocf/resource.d/heartbeat"  # SUSE

curl -fsSL -o "${RA_PATH}/iSCSILogicalUnit" \
  https://raw.githubusercontent.com/ClusterLabs/resource-agents/main/heartbeat/iSCSILogicalUnit.in
sed -i '1s|@BASH_SHELL@|/bin/bash|' "${RA_PATH}/iSCSILogicalUnit"
chmod 0755 "${RA_PATH}/iSCSILogicalUnit"

⚠️ WARNING: Overwriting the distribution-installed resource agent means a resource-agents package update (dnf update, apt upgrade, zypper update) can replace the file with the older packaged version. Hold the resource-agents package at its current version so the package manager does not overwrite the manually installed agent:

dnf versionlock add resource-agents     # RHEL and derivatives, requires the python3-dnf-plugin-versionlock package
apt-mark hold resource-agents           # Debian and Ubuntu
zypper addlock resource-agents          # SUSE

Release the lock before upgrading, then re-run the install script to restore the updated resource agent:

dnf versionlock delete resource-agents  # RHEL and derivatives
apt-mark unhold resource-agents         # Debian and Ubuntu
zypper removelock resource-agents       # SUSE

Emulating block size with Pacemaker

The iSCSILogicalUnit resource agent supports block_size, liot_bstype, scst_bstype, and write_cache parameters. These parameters require the updated resource agent installed earlier.

targetcli

primitive p_iscsi_target ocf:heartbeat:iSCSITarget \
    params iqn="iqn.2024-01.com.example:target1" \
           implementation="lio-t" \
           portals="0.0.0.0:3260"

primitive p_iscsi_lun0 ocf:heartbeat:iSCSILogicalUnit \
    params target_iqn="iqn.2024-01.com.example:target1" \
           implementation="lio-t" \
           liot_bstype="fileio" \
           lun=1 \
           path="/dev/drbd0" \
           block_size=512

SCST

primitive p_iscsi_target ocf:heartbeat:iSCSITarget \
    params iqn="iqn.2024-01.com.example:target1" \
           implementation="scst" \
           portals="0.0.0.0:3260"

primitive p_iscsi_lun0 ocf:heartbeat:iSCSILogicalUnit \
    params target_iqn="iqn.2024-01.com.example:target1" \
           implementation="scst" \
           scst_bstype="vdisk_fileio" \
           lun=1 \
           path="/dev/drbd0" \
           block_size=512

Emulating block size with DRBD Reactor

In a DRBD Reactor promoter plugin configuration, the block_size and iSCSI backstore type parameters are passed as resource agent attributes.

targetcli

[[promoter]]
[promoter.resources.iscsi-liot]
start = [
    "ocf:heartbeat:portblock pblock0 action=block ip=192.168.1.100 portno=3260 protocol=tcp",
    "ocf:heartbeat:IPaddr2 vip0 ip=192.168.1.100 cidr_netmask=24",
    "ocf:heartbeat:iSCSITarget target implementation=lio-t iqn=iqn.2024-01.com.example:target1 portals=0.0.0.0:3260",
    "ocf:heartbeat:iSCSILogicalUnit lun0 block_size=512 implementation=lio-t liot_bstype=fileio lun=1 path=/dev/drbd/by-res/iscsi-liot/1 target_iqn=iqn.2024-01.com.example:target1",
    "ocf:heartbeat:portblock punblock0 action=unblock ip=192.168.1.100 portno=3260 protocol=tcp",
]

SCST

[[promoter]]
[promoter.resources.iscsi-scst]
start = [
    "ocf:heartbeat:portblock pblock0 action=block ip=192.168.1.100 portno=3260 protocol=tcp",
    "ocf:heartbeat:IPaddr2 vip0 ip=192.168.1.100 cidr_netmask=24",
    "ocf:heartbeat:iSCSITarget target implementation=scst iqn=iqn.2024-01.com.example:target1 portals=0.0.0.0:3260",
    "ocf:heartbeat:iSCSILogicalUnit lun0 block_size=512 implementation=scst scst_bstype=vdisk_fileio lun=1 path=/dev/drbd/by-res/iscsi-scst/1 target_iqn=iqn.2024-01.com.example:target1",
    "ocf:heartbeat:portblock punblock0 action=unblock ip=192.168.1.100 portno=3260 protocol=tcp",
]

Emulating block size with LINSTOR Gateway

Added as additional parameters in version 2.2.0, LINSTOR Gateway supports the --block-size, --backstore-type, and --write-cache flags on the iscsi create command. Run linstor-gateway version to check the installed version.

💡 TIP: LINSTOR Gateway defaults to write-through caching, a safe default for synchronous replication with DRBD.

targetcli

linstor-gateway iscsi create iqn.2024-01.com.example:target1 \
  192.168.1.100/24 10G \
  --implementation lio-t \
  --backstore-type fileio \
  --block-size 512 \
  --resource-group my-rg

SCST

linstor-gateway iscsi create iqn.2024-01.com.example:target1 \
  192.168.1.100/24 10G \
  --implementation scst \
  --backstore-type vdisk_fileio \
  --block-size 512 \
  --resource-group my-rg

The --block-size, --backstore-type, and --write-cache options apply to all LUNs on the target, including volumes added later with add-volume.

📝 NOTE: The --backstore-type flag is only needed when changing the block size from 4K to 512. When changing the block size from 512 to 4K, you do not need to specify --backstore-type. The default block backstore type works without issue in this case.

Testing 512B block size emulation

The following table summarizes the results of LINBIT internal I/O testing with a 4Kn backing device and block_size=512 presented to the initiator:

Test LIO iblock LIO fileio SCST blockio SCST fileio
dd bs=512 oflag=direct I/O error Pass No LUN Pass
dd bs=4096 oflag=direct Pass Pass No LUN Pass
dd bs=1M oflag=direct Pass Pass No LUN Pass
mkfs.ext4 I/O error Pass No LUN Pass
mkfs.xfs I/O error Pass No LUN Pass
Data integrity (sha256sum) n/a Pass No LUN Pass

In testing, the LIO iblock backstore rejected any I/O smaller than the physical sector size of the backing device. The LIO fileio backstore passed, because the kernel page cache absorbed misaligned writes and flushed them in properly aligned chunks. The SCST vdisk_blockio handler refused the configuration as it does not accept a block_size smaller than the backing device sector size. The SCST vdisk_fileio handler showed the same page cache behavior as the LIO fileio backstore.

📝 NOTE: Both fileio backstores present a 512B logical sector size, but a different physical sector size: 512B for LIO fileio, 4096B for SCST vdisk_fileio.

IMPORTANT: These testing results imply compatibility limitations when emulating 512B block size (4Kn -> 512e). For presenting 512n storage as 4K to the initiator (512n -> 4K), you do not need to specify fileio or vdisk_fileio. The Linux block I/O subsystem natively supports a logical block size larger than the physical sector size.


Written by RR, 2026-06-24.

Reviewed by MAT, 2026-06-26.