From 4c669b951900ee80a508166b8c115dca440f6bae Mon Sep 17 00:00:00 2001 From: Ryotaro Banno Date: Thu, 5 Sep 2024 01:08:31 +0000 Subject: [PATCH] fix: lvmd: use 0 as free bytes of thin pool if usage exceeds total capacity In the current implementation, the free capacity of a thin pool is calcualted by subtracting its usage from the overprovisioned total capacity. Since both usage and capacity are represented as unsigned integers, the result wraps around if the usage somehow exceeds the capacity. This comit addresses the issue by comparing the two values before performing the subtraction. Signed-off-by: Ryotaro Banno --- internal/lvmd/lvservice.go | 7 ++++--- internal/lvmd/util.go | 11 +++++++++++ internal/lvmd/vgservice.go | 7 ++++--- 3 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 internal/lvmd/util.go diff --git a/internal/lvmd/lvservice.go b/internal/lvmd/lvservice.go index 397512ff2..cad569825 100644 --- a/internal/lvmd/lvservice.go +++ b/internal/lvmd/lvservice.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "math" "github.com/topolvm/topolvm/internal/lvmd/command" "github.com/topolvm/topolvm/pkg/lvmd/proto" @@ -79,7 +78,8 @@ func (s *lvService) CreateLV(ctx context.Context, req *proto.CreateLVRequest) (* logger.Error(err, "failed to get free bytes") return nil, status.Error(codes.Internal, err.Error()) } - free = uint64(math.Floor(dc.ThinPoolConfig.OverprovisionRatio*float64(tpu.SizeBytes))) - tpu.VirtualBytes + free = calcThinPoolFreeBytes( + dc.ThinPoolConfig.OverprovisionRatio, tpu.SizeBytes, tpu.VirtualBytes) default: // technically this block will not be hit however make sure we return error // in such cases where deviceclass target is neither thick or thinpool @@ -364,7 +364,8 @@ func (s *lvService) ResizeLV(ctx context.Context, req *proto.ResizeLVRequest) (* logger.Error(err, "failed to get free bytes") return nil, status.Error(codes.Internal, err.Error()) } - free = uint64(math.Floor(dc.ThinPoolConfig.OverprovisionRatio*float64(tpu.SizeBytes))) - tpu.VirtualBytes + free = calcThinPoolFreeBytes( + dc.ThinPoolConfig.OverprovisionRatio, tpu.SizeBytes, tpu.VirtualBytes) default: // technically this block will not be hit however make sure we return error // in such cases where deviceclass target is neither thick or thinpool diff --git a/internal/lvmd/util.go b/internal/lvmd/util.go new file mode 100644 index 000000000..e758470b4 --- /dev/null +++ b/internal/lvmd/util.go @@ -0,0 +1,11 @@ +package lvmd + +import "math" + +func calcThinPoolFreeBytes(overProvisionRatio float64, tpuSizeBytes, tpuVirtualBytes uint64) uint64 { + virtualPoolSize := uint64(math.Floor(overProvisionRatio * float64(tpuSizeBytes))) + if virtualPoolSize <= tpuVirtualBytes { + return 0 + } + return virtualPoolSize - tpuVirtualBytes +} diff --git a/internal/lvmd/vgservice.go b/internal/lvmd/vgservice.go index 20e6638c9..deed6cdf3 100644 --- a/internal/lvmd/vgservice.go +++ b/internal/lvmd/vgservice.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "math" "sync" "github.com/topolvm/topolvm/internal/lvmd/command" @@ -122,7 +121,8 @@ func (s *vgService) GetFreeBytes(ctx context.Context, req *proto.GetFreeBytesReq } // freebytes available in thinpool considering the overprovisionratio - vgFree = uint64(math.Floor(dc.ThinPoolConfig.OverprovisionRatio*float64(tpu.SizeBytes))) - tpu.VirtualBytes + vgFree = calcThinPoolFreeBytes( + dc.ThinPoolConfig.OverprovisionRatio, tpu.SizeBytes, tpu.VirtualBytes) default: return nil, status.Error(codes.Internal, fmt.Sprintf("unsupported device class target: %s", dc.Type)) @@ -185,7 +185,8 @@ func (s *vgService) send(server proto.VGService_WatchServer) error { tpi.MetadataPercent = tpu.MetadataPercent // used for annotating the node for capacity aware scheduling - opb := uint64(math.Floor(dc.ThinPoolConfig.OverprovisionRatio*float64(tpu.SizeBytes))) - tpu.VirtualBytes + opb := calcThinPoolFreeBytes( + dc.ThinPoolConfig.OverprovisionRatio, tpu.SizeBytes, tpu.VirtualBytes) tpi.OverprovisionBytes = opb if dc.Default { res.FreeBytes = opb