Skip to content

fix(cambricon): enforce ResourceQuota for percentage/whole-card memory requests - #2536

Merged
hami-robot[bot] merged 1 commit into
Project-HAMi:masterfrom
adity1raut:fix/cambricon-quota-percentage-bypass
Aug 10, 2026
Merged

fix(cambricon): enforce ResourceQuota for percentage/whole-card memory requests#2536
hami-robot[bot] merged 1 commit into
Project-HAMi:masterfrom
adity1raut:fix/cambricon-quota-percentage-bypass

Conversation

@adity1raut

@adity1raut adity1raut commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What this PR does

Fixes a ResourceQuota bypass for Cambricon when memory is requested by percentage, including the implicit whole-card default a pod gets when it omits the memory field entirely.

Why

Fit() in pkg/device/cambricon/device.go resolves memreq from either an explicit Memreq, or, when that's zero, from MemPercentagereq against the candidate device's Totalmem. Either way, the only thing that value was ever checked against was device capacity (dev.Totalmem-dev.Usedmem < memreq). The namespace ResourceQuota was never consulted on this path.

Concretely: a pod that requests cambricon.com/mlu: 1 with no vmemory field gets Memreq=0, MemPercentagereq=100 from GenerateResourceRequests - that's the normal way to ask for a whole card. Fit resolves that against the real card and hands out the full amount, and the quota manager still records it as used. If the namespace has a tight quota, that one pod consumes it entirely without ever being checked against it, and every subsequent pod in the namespace is denied - including small, well-formed ones that respect the quota.

fitResourceQuota in the webhook can't fix this on its own, because it sums req.Memreq * req.Nums across containers before a node/card is even chosen, and Memreq is 0 for a percentage-based request - the real value only exists once the specific card's Totalmem is known, i.e. inside Fit.

NVIDIA already handles this correctly: it has a local fitQuota helper that resolves the pod's hypothetical total usage and calls FitQuota from inside its own Fit(), right after memreq is resolved. Cambricon didn't have the equivalent.

What changed

  • Added a fitQuota helper to pkg/device/cambricon/device.go, structurally identical to NVIDIA's: it folds the candidate allocation into whatever's already tentatively allocated for the pod (respecting CollapseInitContainerUsage for init-container peak usage), then calls device.GetLocalCache().FitQuota(...).
  • Wired it into Fit() right after memreq is resolved (mirroring where NVIDIA places its own call), before the existing capacity checks. A quota miss now increments ResourceQuotaNotFit and moves on to the next candidate device, same as every other rejection reason in this function.
  • Added TestDevices_Fit_ResourceQuotaWholeCardRequest in pkg/device/cambricon/device_test.go, which seeds a namespace quota via QuotaManager.AddQuota and asserts a whole-card (MemPercentagereq=100) request that exceeds it is now denied with ResourceQuotaNotFit. I checked this test does fail against the pre-fix code (reverted the Fit() change locally and reran it - it comes back fit=true, quota silently ignored).

This only touches the Cambricon backend. The same admission-time gap likely exists for other non-NVIDIA backends that expose a percentage-based memory resource, but I didn't want to bundle a multi-vendor change into one PR - happy to open follow-ups per backend if that's useful.

Verification

  • go build ./...
  • go test ./pkg/device/cambricon/... ./pkg/device/... -short --race -count=1 (all pass, including the new regression test)
  • go vet ./pkg/device/cambricon/...
  • golangci-lint run ./pkg/device/cambricon/... (0 issues)
  • hack/verify-license.sh, hack/verify-import-aliases.sh (pass)
  • No accelerator hardware available - this change is entirely scheduler-side quota bookkeeping (same as the existing NVIDIA fitQuota it mirrors), so I validated it with the unit test above rather than on real MLU hardware.

Fixes #2468

AI assistance disclosure

I ran into this while back in the ResourceQuota test fixtures I touched in #2432, and used Claude Code to help pin down the exact call sites and draft the fix by mirroring NVIDIA's existing fitQuota, plus the regression test. I read through the resulting diff, verified the root cause by reverting the Fit() change and confirming the new test fails without it, and take responsibility for the correctness of both the fix and the test.

Summary by CodeRabbit

  • Bug Fixes
    • Cambricon device allocation now respects namespace resource quotas before approving requests.
    • Whole-card requests using 100% memory are correctly denied when they exceed the configured memory quota.
    • Allocation decisions now provide a quota-specific rejection reason.

…y requests

Fit() resolved memreq from either an explicit Memreq or, when that was
zero, from MemPercentagereq against the candidate device's Totalmem -
but only ever checked the result against device capacity. The
namespace ResourceQuota was never consulted for that path, so a pod
that requests memory by percentage (or omits the memory field, which
defaults to MemPercentagereq=100, i.e. a whole card) is admitted and
its usage charged even when it exceeds the quota. Every other pod in
the namespace is then denied against a quota that one pod already
silently blew through.

NVIDIA already avoids this by calling FitQuota from inside its own
Fit(), once the real card's Totalmem is known and the percentage can
be resolved to an absolute value. This mirrors that same pattern for
Cambricon: resolve memreq first, then check it against the quota
before falling through to the existing capacity checks.

Fixes Project-HAMi#2468

Signed-off-by: Aditya Raut <araut7798@gmail.com>
@hami-robot hami-robot Bot added the size/L label Aug 10, 2026
@github-actions github-actions Bot added the kind/bug Something isn't working label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e79ac818-556f-49d9-8f1b-1ceff272295f

📥 Commits

Reviewing files that changed from the base of the PR and between 8812b79 and ace4fd4.

📒 Files selected for processing (2)
  • pkg/device/cambricon/device.go
  • pkg/device/cambricon/device_test.go

📝 Walkthrough

Walkthrough

Cambricon Fit now checks aggregate memory and core usage against namespace ResourceQuota before device-capacity checks. A regression test verifies that an implicit whole-card request is rejected with ResourceQuotaNotFit.

Changes

Cambricon quota enforcement

Layer / File(s) Summary
Aggregate quota accounting
pkg/device/cambricon/device.go
Adds fitQuota to combine existing allocations with a candidate device and validate aggregate Cambricon memory and core usage through the local quota cache.
Fit admission and regression coverage
pkg/device/cambricon/device.go, pkg/device/cambricon/device_test.go
Fit skips candidates that exceed quota and records ResourceQuotaNotFit. The regression test covers a whole-card request with Memreq: 0 and MemPercentagereq: 100.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

  • Project-HAMi/HAMi issue 2468 — Covers Cambricon quota enforcement for percentage and whole-card memory requests.
  • Project-HAMi/HAMi issue 2363 — Covers Cambricon-specific Fit quota validation.
  • Project-HAMi/HAMi issue 2157 — Covers ResourceQuota enforcement for non-NVIDIA accelerator backends.

Possibly related PRs

Suggested reviewers: wangmin362

Poem

A rabbit checks the quota gate,
While Cambricon cards await their fate.
Memory and cores join the queue,
Whole-card wishes get checked too.
If limits loom, the fit says no—
With tidy reasons logged below.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the Cambricon ResourceQuota fix for percentage-based and whole-card memory requests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.30435% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/device/cambricon/device.go 91.30% 1 Missing and 1 partial ⚠️
Flag Coverage Δ
unittests 62.14% <91.30%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
pkg/device/cambricon/device.go 86.26% <91.30%> (+2.74%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mesutoezdil

Copy link
Copy Markdown
Contributor

/lgtm

@FouoF

FouoF commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

/lgtm

@hami-robot

hami-robot Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: adity1raut, FouoF

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ResourceQuota is not enforced when device memory is requested by percentage or defaulted to the whole card

3 participants