Skip to content

Commit

Permalink
feat: align partition to minimum I/O size
Browse files Browse the repository at this point in the history
Gather information about minimum/optimal I/O size for the disks and
align partitions to either physical block size or min i/o size,
whichever is bigger.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
  • Loading branch information
smira authored and talos-bot committed Aug 4, 2021
1 parent c34b59f commit 87816a8
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 3 deletions.
15 changes: 12 additions & 3 deletions blockdevice/lba/lba.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,27 @@ func (buf *Buffer) Bytes() []byte {
type LBA struct {
PhysicalBlockSize int64
LogicalBlockSize int64
TotalSectors int64
MinimalIOSize int64
OptimalIOSize int64

TotalSectors int64

f *os.File
}

// AlignToPhysicalBlockSize aligns LBA value in LogicalBlockSize multiples to be aligned to PhysicalBlockSize.
func (l *LBA) AlignToPhysicalBlockSize(lba uint64) uint64 {
physToLogical := uint64(l.PhysicalBlockSize / l.LogicalBlockSize)
minIOToLogical := uint64(l.MinimalIOSize / l.LogicalBlockSize)

ratio := physToLogical
if minIOToLogical > ratio {
ratio = minIOToLogical
}

if physToLogical <= 1 {
if ratio <= 1 {
return lba
}

return (lba + physToLogical - 1) / physToLogical * physToLogical
return (lba + ratio - 1) / ratio * ratio
}
36 changes: 36 additions & 0 deletions blockdevice/lba/lba_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import (
"golang.org/x/sys/unix"
)

// These newer ioctl magic values are not available from unix yet.
const (
BLKIOMIN = 0x1278
BLKIOOPT = 0x1279
)

// NewLBA initializes and returns an `LBA`.
func NewLBA(f *os.File) (lba *LBA, err error) {
st, err := f.Stat()
Expand Down Expand Up @@ -40,6 +46,34 @@ func NewLBA(f *os.File) (lba *LBA, err error) {
}
}

var minio int64
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(), BLKIOMIN, uintptr(unsafe.Pointer(&minio))); errno != 0 {
if st.Mode().IsRegular() {
// Not supported, fail over to psize.
minio = psize
} else {
return nil, errors.New("BLKIOMIN failed")
}
}

if minio == 0 {
minio = psize
}

var optio int64
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(), BLKIOOPT, uintptr(unsafe.Pointer(&optio))); errno != 0 {
if st.Mode().IsRegular() {
// Not supported, fail over to psize.
optio = minio
} else {
return nil, errors.New("BLKIOOPT failed")
}
}

if optio == 0 {
optio = minio
}

// Seek to the end to get the size.
size, err := f.Seek(0, 2)
if err != nil {
Expand All @@ -57,6 +91,8 @@ func NewLBA(f *os.File) (lba *LBA, err error) {
lba = &LBA{
PhysicalBlockSize: psize,
LogicalBlockSize: lsize,
MinimalIOSize: minio,
OptimalIOSize: optio,
TotalSectors: tsize,
f: f,
}
Expand Down
17 changes: 17 additions & 0 deletions blockdevice/lba/lba_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,20 @@ func TestAlignToPhysicalBlockSize(t *testing.T) {
assert.EqualValues(t, 8, l.AlignToPhysicalBlockSize(8))
assert.EqualValues(t, 16, l.AlignToPhysicalBlockSize(9))
}

func TestAlignToMinIOkSize(t *testing.T) {
l := lba.LBA{ //nolint: exhaustivestruct
MinimalIOSize: 262144,
PhysicalBlockSize: 512,
LogicalBlockSize: 512,
}

assert.EqualValues(t, 0, l.AlignToPhysicalBlockSize(0))
assert.EqualValues(t, 512, l.AlignToPhysicalBlockSize(1))
assert.EqualValues(t, 512, l.AlignToPhysicalBlockSize(2))
assert.EqualValues(t, 512, l.AlignToPhysicalBlockSize(3))
assert.EqualValues(t, 512, l.AlignToPhysicalBlockSize(4))
assert.EqualValues(t, 512, l.AlignToPhysicalBlockSize(8))
assert.EqualValues(t, 512, l.AlignToPhysicalBlockSize(512))
assert.EqualValues(t, 1024, l.AlignToPhysicalBlockSize(513))
}

0 comments on commit 87816a8

Please sign in to comment.