diff --git a/src/control/cmd/dmg/pool.go b/src/control/cmd/dmg/pool.go index 1c5d42dacaa..6bc0ff90929 100644 --- a/src/control/cmd/dmg/pool.go +++ b/src/control/cmd/dmg/pool.go @@ -193,14 +193,9 @@ type poolCreateCmd struct { func ratio2Percentage(log logging.Logger, tier1, tier2 float64) (p float64) { p = 100.00 - min := storage.MinScmToNVMeRatio * p if tier2 > 0 { p *= tier1 / (tier1 + tier2) - if p < min { - log.Noticef("storage tier ratio is less than %0.2f%%, DAOS performance "+ - "will suffer!", min) - } return } @@ -208,6 +203,38 @@ func ratio2Percentage(log logging.Logger, tier1, tier2 float64) (p float64) { return } +// checkPoolCreateTierRatioWarning examines the pool create response and returns a warning +// message if the tier ratio is below the minimum threshold for PMem mode. Returns empty +// string if no warning is needed (MD-on-SSD mode or acceptable ratio). +func checkPoolCreateTierRatioWarning(resp *control.PoolCreateResp) string { + // Only check for two-tier configurations (SCM/NVMe or metadata/data) + if len(resp.TierBytes) != 2 { + return "" + } + + // Skip if second tier is not configured + if resp.TierBytes[1] == 0 { + return "" + } + + // Skip warning for MD-on-SSD mode - low metadata ratios are normal + if resp.MdOnSsdActive { + return "" + } + + // Calculate actual tier ratio from allocated bytes + percentage := 100.00 * float64(resp.TierBytes[0]) / float64(resp.TierBytes[0]+resp.TierBytes[1]) + minPercentage := storage.MinScmToNVMeRatio * 100.00 + + // Warn if ratio is below minimum for PMem mode (SCM:NVMe) + if percentage < minPercentage { + return fmt.Sprintf("storage tier ratio is less than %0.2f%%, DAOS performance may suffer!", + minPercentage) + } + + return "" +} + // MemRatio can be supplied as two fractions that make up 1 or a single fraction less than 1. // Supply only the first fraction in request and if not set then use the default. func (cmd *poolCreateCmd) setMemRatio(req *control.PoolCreateReq, defVal float32) error { @@ -396,6 +423,11 @@ func (cmd *poolCreateCmd) Execute(args []string) error { return err } + // Check if low tier ratio warning should be shown + if warning := checkPoolCreateTierRatioWarning(resp); warning != "" { + cmd.Notice(warning) + } + var bld strings.Builder if err := pretty.PrintPoolCreateResponse(resp, &bld); err != nil { return err diff --git a/src/control/cmd/dmg/pool_test.go b/src/control/cmd/dmg/pool_test.go index 5eeae387584..294ff51b748 100644 --- a/src/control/cmd/dmg/pool_test.go +++ b/src/control/cmd/dmg/pool_test.go @@ -1,6 +1,6 @@ // // (C) Copyright 2019-2024 Intel Corporation. -// (C) Copyright 2025 Hewlett Packard Enterprise Development LP +// (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP // // SPDX-License-Identifier: BSD-2-Clause-Patent // @@ -1492,3 +1492,103 @@ func TestDmg_PoolListCmd_Errors(t *testing.T) { }) } } + +func Test_checkPoolCreateTierRatioWarning(t *testing.T) { + for name, tc := range map[string]struct { + resp *control.PoolCreateResp + expWarning string + }{ + "PMem mode with low tier ratio - warning expected": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{100 * humanize.GiByte, 10 * humanize.TiByte}, // 0.98% ratio + MdOnSsdActive: false, + }, + expWarning: "storage tier ratio is less than 1.00%, DAOS performance may suffer!", + }, + "PMem mode with acceptable tier ratio - no warning": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{600 * humanize.GiByte, 9400 * humanize.GiByte}, // 6% ratio + MdOnSsdActive: false, + }, + expWarning: "", + }, + "PMem mode at minimum threshold - no warning": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{100 * humanize.GiByte, 9900 * humanize.GiByte}, // 1.0% ratio + MdOnSsdActive: false, + }, + expWarning: "", + }, + "PMem mode just below threshold - warning expected": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{99 * humanize.GiByte, 9901 * humanize.GiByte}, // 0.99% ratio + MdOnSsdActive: false, + }, + expWarning: "storage tier ratio is less than 1.00%, DAOS performance may suffer!", + }, + "MD-on-SSD mode with low tier ratio - no warning": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{142 * humanize.GiByte, 20 * humanize.TiByte}, // ~0.7% ratio + MdOnSsdActive: true, + }, + expWarning: "", + }, + "MD-on-SSD mode with very low tier ratio - no warning": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{10 * humanize.GiByte, 10 * humanize.TiByte}, // ~0.1% ratio + MdOnSsdActive: true, + }, + expWarning: "", + }, + "MD-on-SSD mode with normal tier ratio - no warning": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{1200 * humanize.GiByte, 18800 * humanize.GiByte}, // 6% ratio + MdOnSsdActive: true, + }, + expWarning: "", + }, + "single tier configuration - no warning": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{1000 * humanize.GiByte}, + MdOnSsdActive: false, + }, + expWarning: "", + }, + "no second tier - no warning": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{1000 * humanize.GiByte, 0}, + MdOnSsdActive: false, + }, + expWarning: "", + }, + "three tier configuration - no warning": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{100 * humanize.GiByte, 1000 * humanize.GiByte, 10000 * humanize.GiByte}, + MdOnSsdActive: false, + }, + expWarning: "", + }, + "empty response - no warning": { + resp: &control.PoolCreateResp{ + TierBytes: []uint64{}, + MdOnSsdActive: false, + }, + expWarning: "", + }, + "nil tier bytes - no warning": { + resp: &control.PoolCreateResp{ + TierBytes: nil, + MdOnSsdActive: false, + }, + expWarning: "", + }, + } { + t.Run(name, func(t *testing.T) { + gotWarning := checkPoolCreateTierRatioWarning(tc.resp) + + if diff := cmp.Diff(tc.expWarning, gotWarning); diff != "" { + t.Fatalf("unexpected warning (-want, +got):\n%s", diff) + } + }) + } +}