Skip to content

DAOS-19772 control: Handle non-zero PCI domains in hotplug filter - #19259

Open
tanabarr wants to merge 26 commits into
masterfrom
tanabarr/control-hotplug-pcidomain
Open

tanabarr wants to merge 26 commits into
masterfrom
tanabarr/control-hotplug-pcidomain

Conversation

@tanabarr

@tanabarr tanabarr commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

bdev_busid_range assumed PCI domain 0x0000 and only encoded an 8-bit
bus-ID range. On multi-PCI-segment platforms with non-zero domains,
this silently filtered the wrong bus-IDs (DAOS-19772: a hot-plugged
drive was claimed by the wrong engine).

Add bdev_hotplug_range, a config array of PCI domain + bus-ID-range
entries (one domain per entry; plain hex, no "0x" prefix, matching
ordinary PCI address formatting), replacing the legacy domain-0x00-
only bdev_busid_range (kept for backward compatibility). When unset,
ranges are now auto-derived directly from bdev_list's actual PCI
addresses instead of NUMA-node topology, collapsing only truly-
contiguous bus-IDs so the filter never admits an address absent from
bdev_list; VMD still allows all. Thread the resulting ranges through
to the SPDK JSON config and bio_config.c's hotplug_filter_fn, which
now matches domain+bus-ID per entry instead of a single bus-ID range.

Update daos_server.yml/admin guide to describe only the automatic
behavior; manual syntax is retained only in the design doc and
src/control/server/storage/README.md as an undocumented, experimental
escape hatch. Decimal notation is no longer accepted, hex only.

Add design/control/hotplug-pci-domain-design.md.

Features: control

Steps for the author:

  • Commit message follows the guidelines.
  • Appropriate Features or Test-tag pragmas were used.
  • Appropriate Functional Test Stages were run.
  • At least two positive code reviews including at least one code owner from each category referenced in the PR.
  • Testing is complete. If necessary, forced-landing label added and a reason added in a comment.

After all prior steps are complete:

  • Gatekeeper requested (daos-gatekeeper added as a reviewer).

@tanabarr tanabarr self-assigned this Oct 13, 2026
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Ticket title is 'Hot-plugged drive claimed by the wrong engine'
Status is 'In Review'
Labels: 'request_for_2.8.2'
https://daosio.atlassian.net/browse/DAOS-19772

@daosbuild3

Copy link
Copy Markdown
Collaborator

Test stage Functional Hardware Medium MD on SSD completed with status UNSTABLE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net/job/daos-stack/job/daos//view/change-requests/job/PR-19259/1/testReport/

@tanabarr
tanabarr marked this pull request as ready for review October 15, 2026 12:07
@tanabarr
tanabarr requested review from a team as code owners October 15, 2026 12:07
@tanabarr
tanabarr force-pushed the tanabarr/control-hotplug-pcidomain branch 3 times, most recently from c81a73a to 2dec464 Compare October 15, 2026 13:41
@tanabarr
tanabarr changed the base branch from master to tanabarr/control-spdk-devtype October 15, 2026 13:46
@daosbuild3

Copy link
Copy Markdown
Collaborator

@tanabarr
tanabarr force-pushed the tanabarr/control-hotplug-pcidomain branch from 2dec464 to d225cee Compare October 15, 2026 14:39
@daosbuild3

Copy link
Copy Markdown
Collaborator

Test stage Functional Hardware Medium MD on SSD completed with status UNSTABLE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net/job/daos-stack/job/daos//view/change-requests/job/PR-19259/6/testReport/

@tanabarr

Copy link
Copy Markdown
Contributor Author

CI build 6 known failures with Features: control

  • FTEST_control.DmgNetworkScanTest

Fix validated on Trout, Hotplug polling and replacement works as expected, manual verification process documented in ticket DAOS-19772.

@tanabarr
tanabarr force-pushed the tanabarr/control-hotplug-pcidomain branch from d225cee to 17d0124 Compare October 21, 2026 13:31
@tanabarr
tanabarr requested a review from a team as a code owner October 21, 2026 13:31
kjacque
kjacque previously approved these changes Oct 21, 2026

@kjacque kjacque left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only looked at the applicable commit... LGTM. My one comment is very minor. I guess this will need to be rebased before merging to remove the system erase commit?

@daosbuild3

Copy link
Copy Markdown
Collaborator

Test stage Functional Hardware Medium MD on SSD completed with status UNSTABLE. https://jenkins-3.daos.hpc.amslabs.hpecorp.net/job/daos-stack/job/daos//view/change-requests/job/PR-19259/7/testReport/

Features: control
Signed-off-by: Tom Nabarro <thomas.nabarro@hpe.com>
@tanabarr
tanabarr force-pushed the tanabarr/control-hotplug-pcidomain branch from 17d0124 to c7fa0ff Compare October 22, 2026 10:58
@tanabarr
tanabarr requested review from a team as code owners October 22, 2026 10:58
@tanabarr
tanabarr changed the base branch from tanabarr/control-spdk-devtype to master October 22, 2026 10:59
@tanabarr
tanabarr dismissed kjacque’s stale review October 22, 2026 10:59

The base branch was changed.

Signed-off-by: Tom Nabarro <thomas.nabarro@hpe.com>
Features: control
Signed-off-by: Tom Nabarro <thomas.nabarro@hpe.com>
…in' into tanabarr/control-hotplug-pcidomain

Features: control
Signed-off-by: Tom Nabarro <thomas.nabarro@hpe.com>
@tanabarr
tanabarr requested a review from kjacque November 5, 2026 12:48
@daosbuild3

Copy link
Copy Markdown
Collaborator

@knard38 knard38 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If my concerns are valid they should be fixed before landing

Comment thread src/control/server/storage/provider.go Outdated
@@ -633,9 +621,9 @@ func BdevWriteConfigRequestFromConfig(ctx context.Context, log logging.Logger, c

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding, the previous hwloc-based derivation returned the bus range of the PCI host bridges of the whole NUMA node, so the filter did not depend on the bdev tiers. Now it is derived from bdev_list, which only lists the devices of one tier, and setHotplugRanges() is only called for the first bdev tier (idx != 0 is skipped).

For example, with the configuration below the filter written to daos_nvme.conf is only 0000:e3, so the SSDs of the second tier (0000:e4, 0000:e5 and 0000:e6) would be refused by their own engine.

storage:
- class: ram
  scm_mount: /mnt/dram1
- class: nvme                  # first bdev tier  (idx 0)
  bdev_roles: [wal]
  bdev_list: ["0000:e3:00.0"]
- class: nvme                  # second bdev tier (idx 1)
  bdev_roles: [meta, data]
  bdev_list: ["0000:e4:00.0", "0000:e5:00.0", "0000:e6:00.0"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

// TestStorage_BdevHotplugRange_JSON is a regression test for JSON marshal/unmarshal of
// BdevHotplugRange.
func TestStorage_BdevHotplugRange_JSON(t *testing.T) {
hr, err := NewBdevHotplugRange("0x01:0x00-0xFF")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my investigation with copilot, this test fails on the current head: the grammar of this PR no longer accepts the 0x prefix, so NewBdevHotplugRange("0x01:0x00-0xFF") returns parse identifier "0x01", expected plain hex notation and the test stops at its first t.Fatal. The Jenkins Unit Test stage of the head fails on exactly this test.

Suggested change
hr, err := NewBdevHotplugRange("0x01:0x00-0xFF")
hr, err := NewBdevHotplugRange("01:00-FF")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, non-blocking: from copilot, a few statements of the design document and of the admin guide do not seem to match the branch (each checked by copilot against the current head):

  • Section 5 and the PR description say the manual syntax is documented in src/control/server/storage/README.md, but that file is not part of the PR and does not exist in the repository, so this document is currently the only place describing bdev_hotplug_range. Should the README be added, or the references removed?
  • Section 7 says daos_server.yml gains a bdev_hotplug_range parameter, which contradicts section 5 and the diff (the diff only removes the bdev_busid_range lines from daos_server.yml).
  • Smaller inaccuracies: section 4.1 names setHotplugRange() (the function is setHotplugRanges); section 4.4 calls spdk_json_decode_array an existing pattern of bio_config.c (this PR adds its only use, in decode_daos_array); section 8 lists overlapping-range validation and C-side tests that I could not find in the PR; section 6 says the file is regenerated "on every engine start" (it is regenerated when the old file cannot be parsed, or at format).
  • The admin guide (docs/admin/administration.md) says the filter is "precise to exactly the devices the engine was formatted with" and "always succeeds"; this does not hold for engines with several bdev tiers (see my comment on provider.go) nor beyond 64 entries (HOTPLUG_FILTER_MAX_ENTRIES in bio_config.c).
  • The legacy bdev_busid_range still works as an alias of domain 0000 but is no longer documented in daos_server.yml or the admin guide, and its use is only logged at DEBUG. Would a one-line "deprecated, applies to domain 0000 only" note in daos_server.yml (and perhaps a NOTICE in the log) help people who still have it in their configuration?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

@kjacque kjacque left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the format updates. I think the issues Cedric found are valid and should be addressed.

Comment thread src/control/server/storage/config.go Outdated
Comment on lines +885 to +889
if strings.Contains(parts[0], "-") {
return nil, errors.Errorf(
"invalid hotplug range domain %q in %q, only a single domain is supported "+
"per entry", parts[0], entryStr)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be detected as simply a bad string format when the domain value is parsed, right? I don't think it needs to be called out specifically.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

Comment thread src/control/server/storage/provider.go Outdated
Comment on lines +580 to +584
ranges, err := deriveHotplugRangesFromBdevList(tier.Bdev.DeviceList)
if err != nil {
return errors.Wrapf(err, "get busid range limits")
return errors.Wrap(err, "derive hotplug filter ranges from bdev_list")
}
req.HotplugRanges = ranges

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Cedric's comment is right, this is only getting the hotplug range for a single tier and overriding the range list. I think it would be fine to append them instead, since the caller is looping over the tiers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

Features: control
Signed-off-by: Tom Nabarro <thomas.nabarro@hpe.com>
Comment on lines +599 to 601
log.Debug("auto-deriving hotplug filter ranges from bdev_list of all bdev tiers")
ranges, err := deriveHotplugRangesFromBdevList(bdevCfgs.NVMeBdevs())
if err != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my investigation with copilot, your fix introduced a regression with emulated dev tiers and copilot proposed the following fix which makes sense to me:

Suggested change
log.Debug("auto-deriving hotplug filter ranges from bdev_list of all bdev tiers")
ranges, err := deriveHotplugRangesFromBdevList(bdevCfgs.NVMeBdevs())
if err != nil {
if !bdevCfgs.HaveRealNVMe() && bdevCfgs.HaveEmulatedNVMe() {
// Only emulated bdevs (class file or kdev): there is no PCI address to derive a filter from.
log.Debug("emulated bdevs only: no hotplug filter ranges configured")
req.HotplugRanges = nil
return nil
}
log.Debug("auto-deriving hotplug filter ranges from bdev_list of all bdev tiers")
ranges, err := deriveHotplugRangesFromBdevList(bdevCfgs.NVMeBdevs())
if err != nil {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you, resolved

Comment thread src/control/server/storage/provider.go Outdated
tier.Bdev.BusidRange.IsZero()) {
continue
}
if manualTier != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT, it could be more user friendly to have this check in TierConfigs.Validate() to inform the user as soon as possible that a manual bdev_hotplug_range (or the legacy bdev_busid_range) is tolerated on one bdev tier only per engine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

Features: control
Signed-off-by: Tom Nabarro <thomas.nabarro@hpe.com>
@daosbuild3

Copy link
Copy Markdown
Collaborator

@tanabarr
tanabarr requested review from kjacque and knard38 November 6, 2026 13:20
@tanabarr

tanabarr commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@kjacque @knard38 have resolved comments, please could you rereview

knard38
knard38 previously approved these changes Nov 6, 2026

@knard38 knard38 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

kjacque
kjacque previously approved these changes Nov 6, 2026
@daltonbohning daltonbohning added the release-3.0.0 Targeted for release 3.0.0 label Nov 6, 2026
@daltonbohning

Copy link
Copy Markdown
Contributor

@tanabarr Requested to land and will fix clang-format in a separate PR.

@mchaarawi

Copy link
Copy Markdown
Contributor

@tanabarr Requested to land and will fix clang-format in a separate PR.

why? can we please just fix it here

mjmac
mjmac previously approved these changes Nov 7, 2026

@mjmac mjmac left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving because I think this is a much better approach now and I have limited review bandwidth for the next week or two. It does seem like the BdevHotplugRange implementation could be cleaned up to match the C struct, though, unless I'm missing something.

Also, it seems like parts of the design document were written from the perspective of how the design was changed from the first approach instead of what it changes from master. That's not really useful context to include in the final result, IMO.

type BdevHotplugRange struct {
LowAddress hardware.PCIAddress
HighAddress hardware.PCIAddress
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why use two PCIAddress members instead of something like this, which also matches the C struct:

type BdevHotplugRange struct {
        Domain    uint16
        BusBegin  uint8
        BusEnd    uint8
}

I'm all for type reuse when it makes sense, but the current type has unused fields and the possibility of accidentally setting different domains on the low/high addresses.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you . I will follow up on your review comments after landing

@tanabarr
tanabarr requested a review from a team November 7, 2026 11:03
@tanabarr

tanabarr commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

@tanabarr Requested to land and will fix clang-format in a separate PR.

why? can we please just fix it here

Sure I will push formatting fix, hopefully we can land on Monday without having to get reviews again given it's just clang formatting that will be fixed and we already have three reviews? @daltonbohning does that sound okay?

Signed-off-by: Tom Nabarro <thomas.nabarro@hpe.com>
@tanabarr
tanabarr dismissed stale reviews from mjmac, kjacque, and knard38 via dc6e047 November 7, 2026 13:47
@tanabarr
tanabarr requested review from kjacque, knard38 and mjmac November 7, 2026 13:48

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

forced-landing The PR has known failures or has intentionally reduced testing, but should still be landed. release-3.0.0 Targeted for release 3.0.0

Development

Successfully merging this pull request may close these issues.

8 participants