Repository: koordinator-sh/koordinator

Issue:
koord-scheduler supports NUMA Topology Alignment policy
<!-- Please only use this template for submitting proposal -->

**What is your proposal**:

The Topology Manager is a kubelet component that aims to coordinate the set of components that are responsible for these optimizations. After Topology Manager was introduced the problem of launching pod in the cluster where worker nodes have different NUMA topology and different amount of resources in that topology became actual. The Pod could be scheduled in the node where the total amount of resources is enough, but resource distribution could not satisfy the appropriate Topology policy. 

When allocating CPUs for LSE/LSR Pods to implement static binding, NUMA Topology also needs to be considered.

More details please refer to #96 

TODO:
- [x] koordlet reports complete NUMA Node information
- [x] koordlet supports NUMA Share Pool
- [x] koord-scheduler supports NUMA Topology Alignment scheduling with CPU/Memory
- [x] koord-scheduler supports scoring
- [x] [koord-scheduler supports numa-node level scoring](https://github.com/koordinator-sh/koordinator/issues/1680)
- [x] [koord-scheduler supports NUMA Topology Alignment scheduling with Device](https://github.com/koordinator-sh/koordinator/pull/1774)
- [x] koord-scheduler supports Pod allocate from Reservation according NUMA Topology
- [x] koord-scheduler supports preemption according NUMA Topology

Discussion:
@FillZpp: /milestone v1.2@AdrianMachao: do you need help? i can do one task@songtao98:  - [ ] koord-scheduler supports Pod allocate from Reservation according NUMA Topology 1.6
- [ ]  koord-scheduler supports preemption according NUMA Topology someday@songtao98:  - [ ] koord-scheduler supports Pod allocate from Reservation according NUMA Topology 1.6
- [ ]  koord-scheduler supports preemption according NUMA Topology someday@ZiMengSheng: Already implemented but lack a concept doc!

Solution patch:
diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES
index 386942ae..2308f03d 100644
--- a/OWNERS_ALIASES
+++ b/OWNERS_ALIASES
@@ -12,6 +12,7 @@ aliases:
   scheduler-approvers:
     - eahydra
     - buptcozy
+    - ZiMengSheng
 
   slo-controller-approvers:
     - jasonliu747
diff --git a/pkg/scheduler/frameworkext/eventhandlers/reservation_handler.go b/pkg/scheduler/frameworkext/eventhandlers/reservation_handler.go
index a766f5c3..d238395b 100644
--- a/pkg/scheduler/frameworkext/eventhandlers/reservation_handler.go
+++ b/pkg/scheduler/frameworkext/eventhandlers/reservation_handler.go
@@ -144,19 +144,12 @@ func generatePodEventOnReservationLevel(errorMsg string) (string, bool) {
 	// for reservation total item
 	reserveTotalRe := regexp.MustCompile("^([0-9]+) Reservation\\(s\\) matched owner total$")
 
+	// for node related item
+	reserveNodeDetailRe := regexp.MustCompile("^([0-9]+ Reservation\\(s\\)) (for node reason that .*)$")
+
 	// for reservation detail item
 	reserveDetailRe := regexp.MustCompile("^([0-9]+) Reservation\\(s\\) .*$")
 
-	// for affinity item of node level
-	affinityPatterns := []string{
-		"^([0-9]+) node\\(s\\) (didn't match pod topology spread constraints \\(missing required label\\))",
-		"^([0-9]+) node\\(s\\) (didn't match pod topology spread constraints)",
-		"^([0-9]+) node\\(s\\) (didn't satisfy existing pods anti-affinity rules)",
-		"^([0-9]+) node\\(s\\) (didn't match pod affinity rules)",
-		"^([0-9]+) node\\(s\\) (didn't match pod anti-affinity rules)",
-	}
-	affinityDetailRe := regexp.MustCompile(strings.Join(affinityPatterns, "|"))
-
 	for _, item := range detailSplit {
 		trimItem := strings.TrimSpace(item)
 		totalStr := reserveTotalRe.FindAllStringSubmatch(trimItem, -1)
@@ -167,35 +160,27 @@ func generatePodEventOnReservationLevel(errorMsg string) (string, bool) {
 			if total, err = strconv.ParseInt(totalStr[0][1], 10, 64); err != nil {
 				return "", false
 			}
-		} else if reserveDetailRe.MatchString(trimItem) {
-			// not total item, append to details, e.g. " 1 Reservation(s) ..."
-
-			// for 1 Reservation(s) Insufficient nvidia, replace nvidia with nvidia.com/gpu
-			// TODO support other extend resource fields like kubernetes.io/batch-cpu
-			itemReplaced := strings.Replace(trimItem, "nvidia", "nvidia.com/gpu", -1)
-			resultDetails = append(resultDetails, itemReplaced)
-		} else {
-			// other node items, record affinity errors on reservation level as:
-			// "at least 3 didn't match pod topology spread constraints Reservation(s)"
-			affinityDetailsSubMatch := affinityDetailRe.FindAllStringSubmatch(trimItem, -1)
-			if len(affinityDetailsSubMatch) == 0 {
+		} else if reserveNodeDetailRe.MatchString(trimItem) {
+			// node related item, e.g. "2 Reservation(s) for node reason that node(s) didn't match pod affinity rules"
+			reserveNodeSubMatch := reserveNodeDetailRe.FindStringSubmatch(trimItem)
+			if len(reserveNodeSubMatch) <= 1 {
 				continue
 			}
-			for _, submatch := range affinityDetailsSubMatch {
-				if len(submatch) <= 1 {
+			// expect: ["2 Reservation(s)", "didn't match pod affinity rules"]
+			nodeReasonWords := make([]string, 0, len(reserveNodeSubMatch)-1)
+			for _, vv := range reserveNodeSubMatch[1:] {
+				if vv == "" {
 					continue
 				}
-				r := &strings.Builder{}
-				r.WriteString("at least ")
-				for _, vv := range submatch[1:] {
-					if vv == "" {
-						continue
-					}
-					r.WriteString(vv + " ")
-				}
-				r.WriteString("Reservation(s)")
-				resultDetails = append(resultDetails, r.String())
+				nodeReasonWords = append(nodeReasonWords, vv)
 			}
+			resultDetails = append(resultDetails, strings.Join(nodeReasonWords, " "))
+		} else if reserveDetailRe.MatchString(trimItem) {
+			// reservation itself item, append to details, e.g. " 1 Reservation(s) ..."
+			// for 1 Reservation(s) Insufficient nvidia, replace nvidia with nvidia.com/gpu
+			// TODO support other extend resource fields like kubernetes.io/batch-cpu
+			itemReplaced := strings.Replace(trimItem, "nvidia", "nvidia.com/gpu", -1)
+			resultDetails = append(resultDetails, itemReplaced)
 		}
 	}
 
diff --git a/pkg/scheduler/plugins/deviceshare/plugin.go b/pkg/scheduler/plugins/deviceshare/plugin.go
index 3706fc20..aaa4242b 100644
--- a/pkg/scheduler/plugins/deviceshare/plugin.go
+++ b/pkg/scheduler/plugins/deviceshare/plugin.go
@@ -312,6 +312,7 @@ func (p *Plugin) Filter(ctx context.Context, cycleState *framework.CycleState, p
 
 	nodeDeviceInfo.lock.RLock()
 	defer nodeDeviceInfo.lock.RUnlock()
+	// TODO 当 NUMA 策略不为空时，关于 NUMA 下设备是否能分配其实已经在 NodeNUMAResource 的 FilterByNUMANode 中调用过，这里存在重复调用，待优化
 	allocateResult, status := p.tryAllocateFromReservation(allocator, state, restoreState, restoreState.matched, node, preemptible, state.hasReservationAffinity)
 	if !status.IsSuccess() {
 		return status
diff --git a/pkg/scheduler/plugins/deviceshare/reservation.go b/pkg/scheduler/plugins/deviceshare/reservation.go
index d384d952..f508c7a5 100644
--- a/pkg/scheduler/plugins/deviceshare/reservation.go
+++ b/pkg/scheduler/plugins/deviceshare/reservation.go
@@ -252,7 +252,7 @@ func (p *Plugin) makeReasonsByReservation(reservationReasons []*framework.Status
 	var reasons []string
 	for _, status := range reservationReasons {
 		for _, r := range status.Reasons() {
-			reasons = append(reasons, fmt.Sprintf("Reservation(s) %s", r))
+			reasons = append(reasons, reservationutil.NewReservationReason(r))
 		}
 	}
 	return reasons
diff --git a/pkg/scheduler/plugins/nodenumaresource/node_allocation.go b/pkg/scheduler/plugins/nodenumaresource/node_allocation.go
index 9cd352af..e2a45217 100644
--- a/pkg/scheduler/plugins/nodenumaresource/node_allocation.go
+++ b/pkg/scheduler/plugins/nodenumaresource/node_allocation.go
@@ -88,6 +88,18 @@ func (n *NodeAllocation) getCPUs(podUID types.UID) (cpuset.CPUSet, bool) {
 	return request.CPUSet, ok
 }
 
+func (n *NodeAllocation) getNUMAResource(podUID types.UID) (map[int]corev1.ResourceList, bool) {
+	request, ok := n.allocatedPods[podUID]
+	if !ok {
+		return nil, false
+	}
+	numaResource := map[int]corev1.ResourceList{}
+	for _, numaNodeResource := range request.NUMANodeResources {
+		numaResource[numaNodeResource.Node] = numaNodeResource.Resources
+	}
+	return numaResource, ok
+}
+
 func (n *NodeAllocation) addCPUs(cpuTopology *CPUTopology, podUID types.UID, cpuset cpuset.CPUSet, exclusivePolicy schedulingconfig.CPUExclusivePolicy) {
 	n.addPodAllocation(&PodAllocation{
 		UID:                podUID,
diff --git a/pkg/scheduler/plugins/nodenumaresource/plugin.go b/pkg/scheduler/plugins/nodenumaresource/plugin.go
index 6cd2aaa7..8c5c850a 100644
--- a/pkg/scheduler/plugins/nodenumaresource/plugin.go
+++ b/pkg/scheduler/plugins/nodenumaresource/plugin.go
@@ -23,10 +23,11 @@ import (
 	nrtv1alpha1 "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/apis/topology/v1alpha1"
 	topologylister "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/generated/listers/topology/v1alpha1"
 	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
 	quotav1 "k8s.io/apiserver/pkg/quota/v1"
+	"k8s.io/klog/v2"
 	resourceapi "k8s.io/kubernetes/pkg/api/v1/resource"
 	"k8s.io/kubernetes/pkg/scheduler/framework"
 
@@ -65,6 +66,7 @@ var (
 	_ framework.PreBindPlugin   = &Plugin{}
 
 	_ frameworkext.ReservationRestorePlugin    = &Plugin{}
+	_ frameworkext.ReservationFilterPlugin     = &Plugin{}
 	_ frameworkext.ReservationPreBindPlugin    = &Plugin{}
 	_ topologymanager.NUMATopologyHintProvider = &Plugin{}
 )
@@ -185,6 +187,8 @@ type preFilterState struct {
 	podNUMAExclusive            extension.NumaTopologyExclusive
 	numCPUsNeeded               int
 	allocation                  *PodAllocation
+
+	hasReservationAffinity bool
 }
 
 func (s *preFilterState) Clone() framework.StateData {
@@ -197,6 +201,7 @@ func (s *preFilterState) Clone() framework.StateData {
 		preferredCPUExclusivePolicy: s.preferredCPUExclusivePolicy,
 		numCPUsNeeded:               s.numCPUsNeeded,
 		allocation:                  s.allocation,
+		hasReservationAffinity:      s.hasReservationAffinity,
 	}
 	return ns
 }
@@ -236,13 +241,18 @@ func (p *Plugin) PreFilter(ctx context.Context, cycleState *framework.CycleState
 		})
 		return nil, nil
 	}
+	reservationAffinity, err := reservationutil.GetRequiredReservationAffinity(pod)
+	if err != nil {
+		return nil, framework.NewStatus(framework.UnschedulableAndUnresolvable, err.Error())
+	}
 	requestedCPU := requests.Cpu().MilliValue()
 	state := &preFilterState{
-		requestCPUBind:        false,
-		requests:              requests,
-		numCPUsNeeded:         int(requestedCPU / 1000),
-		podNUMATopologyPolicy: numaSpec.NUMATopologyPolicy,
-		podNUMAExclusive:      numaSpec.SingleNUMANodeExclusive,
+		requestCPUBind:         false,
+		requests:               requests,
+		numCPUsNeeded:          int(requestedCPU / 1000),
+		podNUMATopologyPolicy:  numaSpec.NUMATopologyPolicy,
+		podNUMAExclusive:       numaSpec.SingleNUMANodeExclusive,
+		hasReservationAffinity: reservationAffinity != nil,
 	}
 	if AllowUseCPUSet(pod) {
 		cpuBindPolicy := schedulingconfig.CPUBindPolicy(resourceSpec.PreferredCPUBindPolicy)
@@ -339,14 +349,29 @@ func (p *Plugin) Filter(ctx context.Context, cycleState *framework.CycleState, p
 		}
 
 		if requiredCPUBindPolicy != "" && numaTopologyPolicy == extension.NUMATopologyPolicyNone {
-			resourceOptions, err := p.getResourceOptions(cycleState, state, node, pod, requestCPUBind, topologymanager.NUMATopologyHint{}, topologyOptions)
+			resourceOptions, err := p.getResourceOptions(state, node, pod, requestCPUBind, topologymanager.NUMATopologyHint{}, topologyOptions)
 			if err != nil {
 				return framework.AsStatus(err)
 			}
-			_, err = p.resourceManager.Allocate(node, pod, resourceOptions)
-			if err != nil {
-				return framework.NewStatus(framework.Unschedulable, err.Error())
+
+			reservationRestoreState := getReservationRestoreState(cycleState)
+			restoreState := reservationRestoreState.getNodeState(node.Name)
+
+			podAllocation, status := tryAllocateFromReservation(p.resourceManager, restoreState, resourceOptions, restoreState.matched, pod, node)
+			if !status.IsSuccess() {
+				return status
+			}
+			if podAllocation != nil {
+				return nil
+			}
+			resourceOptions.requiredResources = nil
+			resourceOptions.reusableResources = appendAllocated(nil, restoreState.mergedUnmatchedUsed)
+			resourceOptions.preferredCPUs = cpuset.NewCPUSet()
+			_, status = p.resourceManager.Allocate(node, pod, resourceOptions)
+			if !status.IsSuccess() {
+				return status
 			}
+			return nil
 		}
 	}
 
@@ -392,7 +417,7 @@ func (p *Plugin) filterAmplifiedCPUs(podRequestMilliCPU int64, nodeInfo *framewo
 	return nil
 }
 
-func (p *Plugin) Reserve(ctx context.Context, cycleState *framework.CycleState, pod *corev1.Pod, nodeName string) *framework.Status {
+func (p *Plugin) FilterReservation(ctx context.Context, cycleState *framework.CycleState, pod *corev1.Pod, reservationInfo *frameworkext.ReservationInfo, nodeName string) *framework.Status {
 	state, status := getPreFilterState(cycleState)
 	if !status.IsSuccess() {
 		return status
@@ -405,6 +430,7 @@ func (p *Plugin) Reserve(ctx context.Context, cycleState *framework.CycleState,
 	if err != nil {
 		return framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))
 	}
+
 	node := nodeInfo.Node()
 	topologyOptions := p.topologyOptionsManager.GetTopologyOptions(node.Name)
 	nodeCPUBindPolicy := extension.GetNodeCPUBindPolicy(node.Labels, topologyOptions.Policy)
@@ -426,16 +452,81 @@ func (p *Plugin) Reserve(ctx context.Context, cycleState *framework.CycleState,
 		}
 	}
 
+	reservationRestoreState := getReservationRestoreState(cycleState)
+	restoreState := reservationRestoreState.getNodeState(nodeName)
+
 	store := topologymanager.GetStore(cycleState)
 	affinity := store.GetAffinity(nodeName)
-	resourceOptions, err := p.getResourceOptions(cycleState, state, node, pod, requestCPUBind, affinity, topologyOptions)
+	resourceOptions, err := p.getResourceOptions(state, node, pod, requestCPUBind, affinity, topologyOptions)
 	if err != nil {
-		return framework.AsStatus(err)
+		return framework.NewStatus(framework.UnschedulableAndUnresolvable, err.Error())
+	}
+
+	matchedReservationAlloc, ok := restoreState.matched[reservationInfo.UID()]
+	if !ok {
+		klog.V(5).Infof("nominated reservation %v doesn't reserve numa resource or cpuset", klog.KObj(reservationInfo.Reservation))
+		return nil
 	}
-	result, err := p.resourceManager.Allocate(node, pod, resourceOptions)
+
+	_, status = tryAllocateFromReservation(p.resourceManager, restoreState, resourceOptions, map[types.UID]reservationAlloc{reservationInfo.UID(): matchedReservationAlloc}, pod, node)
+	return status
+}
+
+func (p *Plugin) Reserve(ctx context.Context, cycleState *framework.CycleState, pod *corev1.Pod, nodeName string) *framework.Status {
+	state, status := getPreFilterState(cycleState)
+	if !status.IsSuccess() {
+		return status
+	}
+	if state.skip {
+		return nil
+	}
+
+	nodeInfo, err := p.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
+	if err != nil {
+		return framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))
+	}
+	node := nodeInfo.Node()
+	topologyOptions := p.topologyOptionsManager.GetTopologyOptions(node.Name)
+	nodeCPUBindPolicy := extension.GetNodeCPUBindPolicy(node.Labels, topologyOptions.Policy)
+	podNUMATopologyPolicy := state.podNUMATopologyPolicy
+	numaTopologyPolicy := getNUMATopologyPolicy(node.Labels, topologyOptions.NUMATopologyPolicy)
+	// we have check in filter, so we will not get error in reserve
+	numaTopologyPolicy, _ = mergeTopologyPolicy(numaTopologyPolicy, podNUMATopologyPolicy)
+	requestCPUBind, status := requestCPUBind(state, nodeCPUBindPolicy)
+	if !status.IsSuccess() {
+		return status
+	}
+	if !requestCPUBind && numaTopologyPolicy == extension.NUMATopologyPolicyNone {
+		return nil
+	}
+
+	if requestCPUBind {
+		if !topologyOptions.CPUTopology.IsValid() {
+			return framework.NewStatus(framework.Error, ErrInvalidCPUTopology)
+		}
+	}
+
+	store := topologymanager.GetStore(cycleState)
+	affinity := store.GetAffinity(nodeName)
+	resourceOptions, err := p.getResourceOptions(state, node, pod, requestCPUBind, affinity, topologyOptions)
 	if err != nil {
 		return framework.AsStatus(err)
 	}
+	reservationRestoreState := getReservationRestoreState(cycleState)
+	restoreState := reservationRestoreState.getNodeState(nodeName)
+	result, status := p.allocateWithNominatedReservation(restoreState, resourceOptions, pod, node)
+	if !status.IsSuccess() {
+		return status
+	}
+	if result == nil {
+		resourceOptions.requiredResources = nil
+		resourceOptions.preferredCPUs = cpuset.NewCPUSet()
+		resourceOptions.reusableResources = appendAllocated(nil, restoreState.mergedUnmatchedUsed)
+		result, status = p.resourceManager.Allocate(node, pod, resourceOptions)
+		if !status.IsSuccess() {
+			return status
+		}
+	}
 	p.resourceManager.Update(nodeName, result)
 	state.allocation = result
 	return nil
@@ -501,26 +592,12 @@ func (p *Plugin) preBindObject(ctx context.Context, cycleState *framework.CycleS
 	return nil
 }
 
-func (p *Plugin) getResourceOptions(cycleState *framework.CycleState, state *preFilterState, node *corev1.Node, pod *corev1.Pod, requestCPUBind bool, affinity topologymanager.NUMATopologyHint, topologyOptions TopologyOptions) (*ResourceOptions, error) {
+func (p *Plugin) getResourceOptions(state *preFilterState, node *corev1.Node, pod *corev1.Pod, requestCPUBind bool, affinity topologymanager.NUMATopologyHint, topologyOptions TopologyOptions) (*ResourceOptions, error) {
 	if err := amplifyNUMANodeResources(node, &topologyOptions); err != nil {
 		return nil, err
 	}
 
-	reservationReservedCPUs, err := p.getReservationReservedCPUs(cycleState, pod, node.Name)
-	if err != nil {
-		return nil, err
-	}
 	amplificationRatio := topologyOptions.AmplificationRatios[corev1.ResourceCPU]
-	reusableResources := map[int]corev1.ResourceList{}
-	if reservationReservedCPUs.Size() > 0 {
-		reservedCPUs := topologyOptions.CPUTopology.CPUDetails.KeepOnly(reservationReservedCPUs)
-		for _, numaNode := range reservedCPUs.NUMANodes().ToSliceNoSort() {
-			cpu := extension.Amplify(int64(reservedCPUs.CPUsInNUMANodes(numaNode).Size()*1000), amplificationRatio)
-			reusableResources[numaNode] = corev1.ResourceList{
-				corev1.ResourceCPU: *resource.NewMilliQuantity(cpu, resource.DecimalSI),
-			}
-		}
-	}
 
 	requests := state.requests
 	if requestCPUBind && amplificationRatio > 1 {
@@ -534,44 +611,20 @@ func (p *Plugin) getResourceOptions(cycleState *framework.CycleState, state *pre
 	}
 
 	options := &ResourceOptions{
-		requests:              requests,
-		originalRequests:      state.requests,
-		numCPUsNeeded:         state.numCPUsNeeded,
-		requestCPUBind:        requestCPUBind,
-		requiredCPUBindPolicy: requiredCPUBindPolicy,
-		cpuBindPolicy:         cpuBindPolicy,
-		cpuExclusivePolicy:    state.preferredCPUExclusivePolicy,
-		preferredCPUs:         reservationReservedCPUs,
-		reusableResources:     reusableResources,
-		hint:                  affinity,
-		topologyOptions:       topologyOptions,
+		requests:                requests,
+		originalRequests:        state.requests,
+		numCPUsNeeded:           state.numCPUsNeeded,
+		requestCPUBind:          requestCPUBind,
+		requiredCPUBindPolicy:   requiredCPUBindPolicy,
+		cpuBindPolicy:           cpuBindPolicy,
+		cpuExclusivePolicy:      state.preferredCPUExclusivePolicy,
+		hint:                    affinity,
+		requiredFromReservation: state.hasReservationAffinity,
+		topologyOptions:         topologyOptions,
 	}
 	return options, nil
 }
 
-func (p *Plugin) getReservationReservedCPUs(cycleState *framework.CycleState, pod *corev1.Pod, nodeName string) (cpuset.CPUSet, error) {
-	var result cpuset.CPUSet
-	if reservationutil.IsReservePod(pod) {
-		return result, nil
-	}
-	nominatedReservation := p.handle.GetReservationNominator().GetNominatedReservation(pod, nodeName)
-	if nominatedReservation == nil {
-		return result, nil
-	}
-
-	allocatedCPUs, _ := p.resourceManager.GetAllocatedCPUSet(nodeName, nominatedReservation.UID())
-	if allocatedCPUs.IsEmpty() {
-		return result, nil
-	}
-	reservationRestoreState := getReservationRestoreState(cycleState)
-	nodeReservationRestoreState := reservationRestoreState.getNodeState(nodeName)
-	reservedCPUs := nodeReservationRestoreState.reservedCPUs[nominatedReservation.UID()]
-	if !reservedCPUs.IsEmpty() && !reservedCPUs.IsSubsetOf(allocatedCPUs) {
-		return result, fmt.Errorf("reservation reserved CPUs are invalid")
-	}
-	return reservedCPUs, nil
-}
-
 func appendResourceSpecIfMissed(object metav1.Object, state *preFilterState, node *corev1.Node, topologyOpts *TopologyOptions) error {
 	cpuBindPolicy, required, err := getCPUBindPolicy(topologyOpts, node, state.requiredCPUBindPolicy, state.preferredCPUBindPolicy)
 	if err != nil {
diff --git a/pkg/scheduler/plugins/nodenumaresource/reservation.go b/pkg/scheduler/plugins/nodenumaresource/reservation.go
index 70dfb50e..37b0f582 100644
--- a/pkg/scheduler/plugins/nodenumaresource/reservation.go
+++ b/pkg/scheduler/plugins/nodenumaresource/reservation.go
@@ -18,24 +18,44 @@ package nodenumaresource
 
 import (
 	"context"
+	"fmt"
 
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
+	quotav1 "k8s.io/apiserver/pkg/quota/v1"
+	"k8s.io/klog/v2"
 	"k8s.io/kubernetes/pkg/scheduler/framework"
 
+	schedulingv1alpha1 "github.com/koordinator-sh/koordinator/apis/scheduling/v1alpha1"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext"
 	"github.com/koordinator-sh/koordinator/pkg/util/cpuset"
+	reservationutil "github.com/koordinator-sh/koordinator/pkg/util/reservation"
 )
 
 const reservationRestoreStateKey = Name + "/reservationRestoreState"
 
 type reservationRestoreStateData struct {
-	skip        bool
 	nodeToState frameworkext.NodeReservationRestoreStates
 }
 
 type nodeReservationRestoreStateData struct {
-	reservedCPUs map[types.UID]cpuset.CPUSet
+	matched   map[types.UID]reservationAlloc
+	unmatched map[types.UID]reservationAlloc
+
+	mergedMatchedAllocatable map[int]corev1.ResourceList
+	mergedMatchedAllocated   map[int]corev1.ResourceList
+	mergedUnmatchedUsed      map[int]corev1.ResourceList
+}
+
+type reservationAlloc struct {
+	rInfo *frameworkext.ReservationInfo
+
+	// TODO CPUSet 预留目前尚未支持, 这里只是计算的时候留一个接口，后续再梳理
+	reservedCPUs cpuset.CPUSet
+
+	allocatable map[int]corev1.ResourceList
+	allocated   map[int]corev1.ResourceList
+	remained    map[int]corev1.ResourceList
 }
 
 func getReservationRestoreState(cycleState *framework.CycleState) *reservationRestoreStateData {
@@ -45,9 +65,7 @@ func getReservationRestoreState(cycleState *framework.CycleState) *reservationRe
 		state, _ = value.(*reservationRestoreStateData)
 	}
 	if state == nil {
-		state = &reservationRestoreStateData{
-			skip: true,
-		}
+		state = &reservationRestoreStateData{}
 	}
 	return state
 }
@@ -65,58 +83,238 @@ func (s *reservationRestoreStateData) getNodeState(nodeName string) *nodeReserva
 	return ns
 }
 
+func (rs *nodeReservationRestoreStateData) mergeReservationAllocations() {
+	unmatched := rs.unmatched
+	if len(unmatched) > 0 {
+		mergedUnmatchedUsed := map[int]corev1.ResourceList{}
+		for _, alloc := range unmatched {
+			used := subtractAllocated(copyAllocated(alloc.allocatable), alloc.remained, true)
+			mergedUnmatchedUsed = appendAllocated(mergedUnmatchedUsed, used)
+		}
+		rs.mergedUnmatchedUsed = mergedUnmatchedUsed
+	}
+
+	matched := rs.matched
+	if len(matched) > 0 {
+		mergedMatchedAllocatable := map[int]corev1.ResourceList{}
+		mergedMatchedAllocated := map[int]corev1.ResourceList{}
+		for _, alloc := range matched {
+			mergedMatchedAllocatable = appendAllocated(mergedMatchedAllocatable, alloc.allocatable)
+			mergedMatchedAllocated = appendAllocated(mergedMatchedAllocated, alloc.allocated)
+		}
+		rs.mergedMatchedAllocatable = mergedMatchedAllocatable
+		rs.mergedMatchedAllocated = mergedMatchedAllocated
+	}
+
+	return
+}
+
+func copyAllocated(m map[int]corev1.ResourceList) map[int]corev1.ResourceList {
+	result := map[int]corev1.ResourceList{}
+	for numaNodeID, numaResource := range m {
+		if numaResource != nil {
+			result[numaNodeID] = numaResource.DeepCopy()
+		}
+	}
+	return result
+}
+
+func subtractAllocated(m map[int]corev1.ResourceList, allocated map[int]corev1.ResourceList, withNonNegativeResult bool) map[int]corev1.ResourceList {
+	if m == nil {
+		m = map[int]corev1.ResourceList{}
+	}
+	for numaNodeID, numaResource := range allocated {
+		if withNonNegativeResult {
+			m[numaNodeID] = quotav1.SubtractWithNonNegativeResult(m[numaNodeID], numaResource)
+		} else {
+			m[numaNodeID] = quotav1.Subtract(m[numaNodeID], numaResource)
+		}
+	}
+	return m
+}
+
+func appendAllocated(m map[int]corev1.ResourceList, allocatedList ...map[int]corev1.ResourceList) map[int]corev1.ResourceList {
+	if m == nil {
+		m = map[int]corev1.ResourceList{}
+	}
+	for _, allocated := range allocatedList {
+		for numaNodeID, numaResource := range allocated {
+			m[numaNodeID] = quotav1.Add(m[numaNodeID], numaResource)
+		}
+	}
+	return m
+}
+
 func (p *Plugin) PreRestoreReservation(ctx context.Context, cycleState *framework.CycleState, pod *corev1.Pod) *framework.Status {
+	return nil
+}
+
+func (p *Plugin) RestoreReservation(ctx context.Context, cycleState *framework.CycleState, podToSchedule *corev1.Pod, matched []*frameworkext.ReservationInfo, unmatched []*frameworkext.ReservationInfo, nodeInfo *framework.NodeInfo) (interface{}, *framework.Status) {
+	nodeName := nodeInfo.Node().Name
+	filterFn := func(reservations []*frameworkext.ReservationInfo) map[types.UID]reservationAlloc {
+		if len(reservations) == 0 {
+			return nil
+		}
+		result := make(map[types.UID]reservationAlloc, len(reservations))
+		for _, rInfo := range reservations {
+			reservePod := rInfo.GetReservePod()
+			var allocatable, allocatedNUMAResource, remained map[int]corev1.ResourceList
+			func() {
+				allocatableNUMAResource, ok := p.resourceManager.GetAllocatedNUMAResource(nodeName, reservePod.UID)
+				if !ok || len(allocatableNUMAResource) == 0 {
+					return
+				}
+				allocatable = allocatableNUMAResource
+				for _, pod := range rInfo.AssignedPods {
+					podNUMAResources, ok := p.resourceManager.GetAllocatedNUMAResource(nodeName, pod.UID)
+					if !ok || len(podNUMAResources) == 0 {
+						continue
+					}
+					allocatedNUMAResource = appendAllocated(allocatedNUMAResource, podNUMAResources)
+				}
+				remained = subtractAllocated(copyAllocated(allocatableNUMAResource), allocatedNUMAResource, false)
+			}()
+			var reservedCPUs cpuset.CPUSet
+			func() {
+				allocatedCPUs, ok := p.resourceManager.GetAllocatedCPUSet(nodeName, reservePod.UID)
+				if !ok || allocatedCPUs.IsEmpty() {
+					return
+				}
+				for _, pod := range rInfo.AssignedPods {
+					podCPUs, ok := p.resourceManager.GetAllocatedCPUSet(nodeName, pod.UID)
+					if !ok || podCPUs.IsEmpty() {
+						continue
+					}
+					allocatedCPUs = allocatedCPUs.Difference(podCPUs)
+				}
+				reservedCPUs = allocatedCPUs
+			}()
+			if allocatable != nil || !reservedCPUs.IsEmpty() {
+				result[rInfo.UID()] = reservationAlloc{
+					rInfo:        rInfo,
+					reservedCPUs: reservedCPUs,
+					allocatable:  allocatable,
+					allocated:    allocatedNUMAResource,
+					remained:     remained,
+				}
+			}
+		}
+		return result
+	}
+	filteredMatched := filterFn(matched)
+	filteredUnmatched := filterFn(unmatched)
+	if len(filteredMatched) == 0 && len(filteredUnmatched) == 0 {
+		return nil, nil
+	}
+	s := &nodeReservationRestoreStateData{
+		matched:   filteredMatched,
+		unmatched: filteredUnmatched,
+	}
+	s.mergeReservationAllocations()
+	return s, nil
+}
+
+func (p *Plugin) FinalRestoreReservation(ctx context.Context, cycleState *framework.CycleState, pod *corev1.Pod, nodeToStates frameworkext.NodeReservationRestoreStates) *framework.Status {
 	state := &reservationRestoreStateData{
-		skip: !AllowUseCPUSet(pod),
+		nodeToState: nodeToStates,
 	}
 	cycleState.Write(reservationRestoreStateKey, state)
 	return nil
 }
 
-func (p *Plugin) RestoreReservation(ctx context.Context, cycleState *framework.CycleState, podToSchedule *corev1.Pod, matched []*frameworkext.ReservationInfo, unmatched []*frameworkext.ReservationInfo, nodeInfo *framework.NodeInfo) (interface{}, *framework.Status) {
-	state := getReservationRestoreState(cycleState)
-	if state.skip {
+func tryAllocateFromReservation(
+	manager ResourceManager,
+	restoreState *nodeReservationRestoreStateData,
+	resourceOptions *ResourceOptions,
+	matchedReservations map[types.UID]reservationAlloc,
+	pod *corev1.Pod,
+	node *corev1.Node,
+) (*PodAllocation, *framework.Status) {
+	if len(matchedReservations) == 0 {
 		return nil, nil
 	}
 
-	nodeName := nodeInfo.Node().Name
-	reservedCPUs := map[types.UID]cpuset.CPUSet{}
-	for _, rInfo := range matched {
-		allocatedCPUs, ok := p.resourceManager.GetAllocatedCPUSet(nodeName, rInfo.UID())
-		if !ok || allocatedCPUs.IsEmpty() {
-			continue
-		}
+	var hasSatisfiedReservation bool
+	var result *PodAllocation
+	var status *framework.Status
 
-		for _, pod := range rInfo.AssignedPods {
-			podCPUs, ok := p.resourceManager.GetAllocatedCPUSet(nodeName, pod.UID)
-			if !ok || podCPUs.IsEmpty() {
-				continue
-			}
+	reusableResource := appendAllocated(nil, restoreState.mergedUnmatchedUsed, restoreState.mergedMatchedAllocated)
 
-			allocatedCPUs = allocatedCPUs.Difference(podCPUs)
-		}
+	var reservationReasons []*framework.Status
+	for _, alloc := range matchedReservations {
+		rInfo := alloc.rInfo
 
-		if !allocatedCPUs.IsEmpty() {
-			reservedCPUs[rInfo.UID()] = allocatedCPUs
-		} else {
-			delete(reservedCPUs, rInfo.UID())
+		reusableResource = appendAllocated(nil, reusableResource, alloc.remained)
+		resourceOptions.reusableResources = reusableResource
+		resourceOptions.preferredCPUs = alloc.reservedCPUs
+
+		allocatePolicy := rInfo.GetAllocatePolicy()
+		if allocatePolicy == schedulingv1alpha1.ReservationAllocatePolicyDefault ||
+			allocatePolicy == schedulingv1alpha1.ReservationAllocatePolicyAligned {
+			result, status = manager.Allocate(node, pod, resourceOptions)
+			if status.IsSuccess() {
+				hasSatisfiedReservation = true
+				break
+			}
+			reservationReasons = append(reservationReasons, status)
+		} else if allocatePolicy == schedulingv1alpha1.ReservationAllocatePolicyRestricted {
+			// TODO 这里仿照 deviceShare 的逻辑这样处理，后续需要再琢磨一下是否有必要
+			_, status := manager.Allocate(node, pod, resourceOptions)
+			if status.IsSuccess() {
+				resourceOptions.requiredResources = alloc.remained
+				if resourceOptions.requestCPUBind && resourceOptions.numCPUsNeeded > alloc.reservedCPUs.Size() {
+					reservationReasons = append(reservationReasons, framework.NewStatus(framework.Unschedulable, "not enough cpus available to satisfy request"))
+					continue
+				}
+				result, status = manager.Allocate(node, pod, resourceOptions)
+				if status.IsSuccess() {
+					if result.CPUSet.Size() > alloc.reservedCPUs.Size() {
+						reservationReasons = append(reservationReasons, framework.NewStatus(framework.Unschedulable, "not enough cpus available to satisfy request"))
+						continue
+					}
+					hasSatisfiedReservation = true
+					break
+				}
+			}
+			reservationReasons = append(reservationReasons, status)
 		}
 	}
-
-	if len(reservedCPUs) == 0 {
-		return nil, nil
+	if !hasSatisfiedReservation && resourceOptions.requiredFromReservation {
+		return nil, framework.NewStatus(framework.Unschedulable, makeReasonsByReservation(reservationReasons)...)
 	}
+	return result, nil
+}
 
-	return &nodeReservationRestoreStateData{
-		reservedCPUs: reservedCPUs,
-	}, nil
+func makeReasonsByReservation(reservationReasons []*framework.Status) []string {
+	var reasons []string
+	for _, status := range reservationReasons {
+		for _, r := range status.Reasons() {
+			reasons = append(reasons, fmt.Sprintf("Reservation(s) %s", r))
+		}
+	}
+	return reasons
 }
 
-func (p *Plugin) FinalRestoreReservation(ctx context.Context, cycleState *framework.CycleState, pod *corev1.Pod, nodeToStates frameworkext.NodeReservationRestoreStates) *framework.Status {
-	state := getReservationRestoreState(cycleState)
-	if state.skip {
-		return nil
+func (p *Plugin) allocateWithNominatedReservation(
+	restoreState *nodeReservationRestoreStateData,
+	resourceOptions *ResourceOptions,
+	pod *corev1.Pod,
+	node *corev1.Node,
+) (*PodAllocation, *framework.Status) {
+	if reservationutil.IsReservePod(pod) {
+		return nil, nil
 	}
-	state.nodeToState = nodeToStates
-	return nil
+	reservation := p.handle.GetReservationNominator().GetNominatedReservation(pod, node.Name)
+	if reservation == nil {
+		if resourceOptions.requiredFromReservation {
+			return nil, framework.NewStatus(framework.Unschedulable, "no nominated reservation")
+		}
+		return nil, nil
+	}
+	nominatedReservationAlloc, ok := restoreState.matched[reservation.UID()]
+	if !ok {
+		klog.V(5).Infof("nominated reservation %v doesn't reserve numa resource or cpuset", klog.KObj(reservation))
+		return nil, nil
+	}
+	return tryAllocateFromReservation(p.resourceManager, restoreState, resourceOptions, map[types.UID]reservationAlloc{reservation.UID(): nominatedReservationAlloc}, pod, node)
 }
diff --git a/pkg/scheduler/plugins/nodenumaresource/resource_manager.go b/pkg/scheduler/plugins/nodenumaresource/resource_manager.go
index 81035487..ceb95b5e 100644
--- a/pkg/scheduler/plugins/nodenumaresource/resource_manager.go
+++ b/pkg/scheduler/plugins/nodenumaresource/resource_manager.go
@@ -28,7 +28,6 @@ import (
 	"k8s.io/apimachinery/pkg/util/sets"
 	quotav1 "k8s.io/apiserver/pkg/quota/v1"
 	"k8s.io/client-go/tools/cache"
-	corehelper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
 	"k8s.io/kubernetes/pkg/scheduler/framework"
 
 	apiext "github.com/koordinator-sh/koordinator/apis/extension"
@@ -40,30 +39,33 @@ import (
 )
 
 type ResourceManager interface {
-	GetTopologyHints(node *corev1.Node, pod *corev1.Pod, options *ResourceOptions, policy apiext.NUMATopologyPolicy) (map[string][]topologymanager.NUMATopologyHint, error)
-	Allocate(node *corev1.Node, pod *corev1.Pod, options *ResourceOptions) (*PodAllocation, error)
+	GetTopologyHints(node *corev1.Node, pod *corev1.Pod, options *ResourceOptions, policy apiext.NUMATopologyPolicy, restoreState *nodeReservationRestoreStateData) (map[string][]topologymanager.NUMATopologyHint, error)
+	Allocate(node *corev1.Node, pod *corev1.Pod, options *ResourceOptions) (*PodAllocation, *framework.Status)
 
 	Update(nodeName string, allocation *PodAllocation)
 	Release(nodeName string, podUID types.UID)
 
 	GetNodeAllocation(nodeName string) *NodeAllocation
 	GetAllocatedCPUSet(nodeName string, podUID types.UID) (cpuset.CPUSet, bool)
+	GetAllocatedNUMAResource(nodeName string, podUID types.UID) (map[int]corev1.ResourceList, bool)
 	GetAvailableCPUs(nodeName string, preferredCPUs cpuset.CPUSet) (availableCPUs cpuset.CPUSet, allocated CPUDetails, err error)
 }
 
 type ResourceOptions struct {
-	numCPUsNeeded         int
-	requestCPUBind        bool
-	requests              corev1.ResourceList
-	originalRequests      corev1.ResourceList
-	requiredCPUBindPolicy bool
-	cpuBindPolicy         schedulingconfig.CPUBindPolicy
-	cpuExclusivePolicy    schedulingconfig.CPUExclusivePolicy
-	preferredCPUs         cpuset.CPUSet
-	reusableResources     map[int]corev1.ResourceList
-	hint                  topologymanager.NUMATopologyHint
-	topologyOptions       TopologyOptions
-	numaScorer            *resourceAllocationScorer
+	numCPUsNeeded           int
+	requestCPUBind          bool
+	requests                corev1.ResourceList
+	originalRequests        corev1.ResourceList
+	requiredCPUBindPolicy   bool
+	cpuBindPolicy           schedulingconfig.CPUBindPolicy
+	cpuExclusivePolicy      schedulingconfig.CPUExclusivePolicy
+	preferredCPUs           cpuset.CPUSet
+	reusableResources       map[int]corev1.ResourceList
+	requiredResources       map[int]corev1.ResourceList
+	requiredFromReservation bool
+	hint                    topologymanager.NUMATopologyHint
+	topologyOptions         TopologyOptions
+	numaScorer              *resourceAllocationScorer
 }
 
 type resourceManager struct {
@@ -121,12 +123,13 @@ func (c *resourceManager) getOrCreateNodeAllocation(nodeName string) *NodeAlloca
 	return v
 }
 
-func (c *resourceManager) GetTopologyHints(node *corev1.Node, pod *corev1.Pod, options *ResourceOptions, policy apiext.NUMATopologyPolicy) (map[string][]topologymanager.NUMATopologyHint, error) {
+func (c *resourceManager) GetTopologyHints(node *corev1.Node, pod *corev1.Pod, options *ResourceOptions, policy apiext.NUMATopologyPolicy, restoreStateData *nodeReservationRestoreStateData) (map[string][]topologymanager.NUMATopologyHint, error) {
 	topologyOptions := options.topologyOptions
 	if len(topologyOptions.NUMANodeResources) == 0 {
 		return nil, fmt.Errorf("insufficient resources on NUMA Node")
 	}
 
+	options.reusableResources = appendAllocated(nil, restoreStateData.mergedUnmatchedUsed, restoreStateData.mergedMatchedAllocatable)
 	totalAvailable, _, err := c.getAvailableNUMANodeResources(node.Name, topologyOptions, options.reusableResources)
 	if err != nil {
 		return nil, err
@@ -135,7 +138,7 @@ func (c *resourceManager) GetTopologyHints(node *corev1.Node, pod *corev1.Pod, o
 		return nil, err
 	}
 
-	hints := generateResourceHints(topologyOptions.NUMANodeResources, options.requests, totalAvailable, options.numaScorer, policy)
+	hints := c.generateResourceHints(node, pod, topologyOptions.NUMANodeResources, options, totalAvailable, policy, restoreStateData)
 	return hints, nil
 }
 
@@ -167,7 +170,7 @@ func (c *resourceManager) trimNUMANodeResources(nodeName string, totalAvailable
 	return nil
 }
 
-func (c *resourceManager) Allocate(node *corev1.Node, pod *corev1.Pod, options *ResourceOptions) (*PodAllocation, error) {
+func (c *resourceManager) Allocate(node *corev1.Node, pod *corev1.Pod, options *ResourceOptions) (*PodAllocation, *framework.Status) {
 	allocation := &PodAllocation{
 		UID:                pod.UID,
 		Namespace:          pod.Namespace,
@@ -184,25 +187,28 @@ func (c *resourceManager) Allocate(node *corev1.Node, pod *corev1.Pod, options *
 	if options.requestCPUBind {
 		cpus, err := c.allocateCPUSet(node, pod, allocation.NUMANodeResources, options)
 		if err != nil {
-			return nil, err
+			return nil, framework.AsStatus(err)
 		}
 		allocation.CPUSet = cpus
 	}
 	return allocation, nil
 }
 
-func (c *resourceManager) allocateResourcesByHint(node *corev1.Node, pod *corev1.Pod, options *ResourceOptions) ([]NUMANodeResource, error) {
+func (c *resourceManager) allocateResourcesByHint(node *corev1.Node, pod *corev1.Pod, options *ResourceOptions) ([]NUMANodeResource, *framework.Status) {
 	if len(options.topologyOptions.NUMANodeResources) == 0 {
-		return nil, fmt.Errorf("insufficient resources on NUMA Node")
+		return nil, framework.NewStatus(framework.Error, "insufficient resources on NUMA Node")
 	}
 
-	totalAvailable, _, err := c.getAvailableNUMANodeResources(node.Name, options.topologyOptions, options.reusableResources)
-	if err != nil {
-		return nil, err
+	totalAvailable := options.requiredResources
+	if len(totalAvailable) == 0 {
+		var err error
+		totalAvailable, _, err = c.getAvailableNUMANodeResources(node.Name, options.topologyOptions, options.reusableResources)
+		if err != nil {
+			return nil, framework.AsStatus(err)
+		}
 	}
-
 	if err := c.trimNUMANodeResources(node.Name, totalAvailable, options); err != nil {
-		return nil, err
+		return nil, framework.AsStatus(err)
 	}
 
 	var requests corev1.ResourceList
@@ -214,7 +220,7 @@ func (c *resourceManager) allocateResourcesByHint(node *corev1.Node, pod *corev1
 
 	result, reasons := tryBestToDistributeEvenly(requests, totalAvailable, options)
 	if len(reasons) > 0 {
-		return nil, framework.NewStatus(framework.Unschedulable, reasons...).AsError()
+		return nil, framework.NewStatus(framework.Unschedulable, reasons...)
 	}
 	return result, nil
 }
@@ -429,6 +435,14 @@ func (c *resourceManager) GetAllocatedCPUSet(nodeName string, podUID types.UID)
 	return nodeAllocation.getCPUs(podUID)
 }
 
+func (c *resourceManager) GetAllocatedNUMAResource(nodeName string, podUID types.UID) (map[int]corev1.ResourceList, bool) {
+	nodeAllocation := c.getOrCreateNodeAllocation(nodeName)
+	nodeAllocation.lock.RLock()
+	defer nodeAllocation.lock.RUnlock()
+
+	return nodeAllocation.getNUMAResource(podUID)
+}
+
 func (c *resourceManager) GetAvailableCPUs(nodeName string, preferredCPUs cpuset.CPUSet) (availableCPUs cpuset.CPUSet, allocated CPUDetails, err error) {
 	topologyOptions := c.topologyOptionsManager.GetTopologyOptions(nodeName)
 	if topologyOptions.CPUTopology == nil {
@@ -457,30 +471,27 @@ func (c *resourceManager) getAvailableNUMANodeResources(nodeName string, topolog
 	return totalAvailable, totalAllocated, nil
 }
 
-func generateResourceHints(numaNodeResources []NUMANodeResource, podRequests corev1.ResourceList, totalAvailable map[int]corev1.ResourceList, numaScorer *resourceAllocationScorer, policy apiext.NUMATopologyPolicy) map[string][]topologymanager.NUMATopologyHint {
-	var resourceNamesByNUMA []corev1.ResourceName
+func (c *resourceManager) generateResourceHints(node *corev1.Node, pod *corev1.Pod, numaNodeResources []NUMANodeResource, options *ResourceOptions, totalAvailable map[int]corev1.ResourceList, policy apiext.NUMATopologyPolicy, restoreState *nodeReservationRestoreStateData) map[string][]topologymanager.NUMATopologyHint {
+	resourceNamesByNUMA := sets.New[corev1.ResourceName]()
 	for _, numaNodeResource := range numaNodeResources {
-		resourceNamesByNUMA = append(resourceNamesByNUMA, quotav1.ResourceNames(numaNodeResource.Resources)...)
+		resourceNamesByNUMA.Insert(quotav1.ResourceNames(numaNodeResource.Resources)...)
 	}
 	numaNodesLackResource := map[corev1.ResourceName][]int{}
-	for _, resourceName := range resourceNamesByNUMA {
+	for resourceName := range resourceNamesByNUMA {
 		for nodeID, numaAvailable := range totalAvailable {
 			if available, ok := numaAvailable[resourceName]; !ok || available.IsZero() {
 				numaNodesLackResource[resourceName] = append(numaNodesLackResource[resourceName], nodeID)
 			}
 		}
 	}
+
 	generator := hintsGenerator{
 		numaNodesLackResource: numaNodesLackResource,
 		minAffinitySize:       make(map[corev1.ResourceName]int),
 		hints:                 map[string][]topologymanager.NUMATopologyHint{},
 	}
-	var memoryResourceNames []corev1.ResourceName
-	for resourceName := range podRequests {
+	for resourceName := range options.requests {
 		generator.minAffinitySize[resourceName] = len(numaNodeResources)
-		if resourceName == corev1.ResourceMemory || corehelper.IsHugePageResourceName(resourceName) {
-			memoryResourceNames = append(memoryResourceNames, resourceName)
-		}
 	}
 
 	numaNodes := make([]int, 0, len(numaNodeResources))
@@ -488,7 +499,7 @@ func generateResourceHints(numaNodeResources []NUMANodeResource, podRequests cor
 		numaNodes = append(numaNodes, v.Node)
 	}
 
-	podRequestResources := framework.NewResource(podRequests)
+	podRequestResources := framework.NewResource(options.requests)
 	totalResourceNames := sets.NewString()
 	bitmask.IterateBitMasks(numaNodes, func(mask bitmask.BitMask) {
 		maskBits := mask.GetBits()
@@ -504,36 +515,50 @@ func generateResourceHints(numaNodeResources []NUMANodeResource, podRequests cor
 			}
 		}
 
+		for resourceName := range options.requests {
+			if _, ok := total[resourceName]; !ok {
+				continue
+			}
+			totalResourceNames.Insert(string(resourceName))
+		}
+
 		var score int64
-		if numaScorer != nil {
+		if options.numaScorer != nil {
 			requested := quotav1.SubtractWithNonNegativeResult(total, available)
-			score, _ = numaScorer.score(framework.NewResource(requested), framework.NewResource(total), podRequestResources)
+			score, _ = options.numaScorer.score(framework.NewResource(requested), framework.NewResource(total), podRequestResources)
 		}
 
-		// verify that for all memory types the node mask has enough allocatable resources
-		generator.generateHints(mask, score, total, available, podRequests, memoryResourceNames...)
-
-		for resourceName := range podRequests {
-			if _, ok := total[resourceName]; ok {
-				totalResourceNames.Insert(string(resourceName))
-			}
-			if resourceName == corev1.ResourceMemory || corehelper.IsHugePageResourceName(resourceName) {
-				continue
+		options.hint = topologymanager.NUMATopologyHint{NUMANodeAffinity: mask}
+		options.requiredResources = nil
+		podAllocation, status := tryAllocateFromReservation(c, restoreState, options, restoreState.matched, pod, node)
+		if !status.IsSuccess() {
+			return
+		}
+		if podAllocation == nil {
+			options.requiredResources = nil
+			options.reusableResources = appendAllocated(nil, restoreState.mergedUnmatchedUsed)
+			options.preferredCPUs = cpuset.NewCPUSet()
+			_, status = c.Allocate(node, pod, options)
+			if !status.IsSuccess() {
+				return
 			}
-			generator.generateHints(mask, score, total, available, podRequests, resourceName)
+		}
+
+		for resourceName := range totalResourceNames {
+			generator.generateHints(mask, score, corev1.ResourceName(resourceName))
 		}
 	})
 
 	// update hints preferred according to multiNUMAGroups, in case when it wasn't provided, the default
 	// behavior to prefer the minimal amount of NUMA nodes will be used
-	for resourceName := range podRequests {
+	for resourceName := range options.requests {
 		minAffinitySize := generator.minAffinitySize[resourceName]
 		for i, hint := range generator.hints[string(resourceName)] {
 			generator.hints[string(resourceName)][i].Preferred = len(hint.NUMANodeAffinity.GetBits()) == minAffinitySize || policy == apiext.NUMATopologyPolicyRestricted
 		}
 	}
 
-	for resourceName := range podRequests {
+	for resourceName := range options.requests {
 		if totalResourceNames.Has(string(resourceName)) {
 			hints := generator.hints[string(resourceName)]
 			if hints == nil {
@@ -552,14 +577,7 @@ type hintsGenerator struct {
 	hints                 map[string][]topologymanager.NUMATopologyHint
 }
 
-func (g *hintsGenerator) generateHints(mask bitmask.BitMask, score int64, totalAllocatable, totalFree corev1.ResourceList, podRequests corev1.ResourceList, resourceNames ...corev1.ResourceName) {
-	for _, resourceName := range resourceNames {
-		total, request := totalAllocatable[resourceName], podRequests[resourceName]
-		if total.Cmp(request) < 0 {
-			return
-		}
-	}
-
+func (g *hintsGenerator) generateHints(mask bitmask.BitMask, score int64, resourceNames ...corev1.ResourceName) {
 	for _, resourceName := range resourceNames {
 		if mask.AnySet(g.numaNodesLackResource[resourceName]) {
 			return
@@ -568,13 +586,6 @@ func (g *hintsGenerator) generateHints(mask bitmask.BitMask, score int64, totalA
 
 	nodeCount := mask.Count()
 
-	for _, resourceName := range resourceNames {
-		free, request := totalFree[resourceName], podRequests[resourceName]
-		if free.Cmp(request) < 0 {
-			return
-		}
-	}
-
 	for _, resourceName := range resourceNames {
 		affinitySize := g.minAffinitySize[resourceName]
 		if nodeCount < affinitySize {
diff --git a/pkg/scheduler/plugins/nodenumaresource/scoring.go b/pkg/scheduler/plugins/nodenumaresource/scoring.go
index 6f74a2a0..64db53b5 100644
--- a/pkg/scheduler/plugins/nodenumaresource/scoring.go
+++ b/pkg/scheduler/plugins/nodenumaresource/scoring.go
@@ -30,6 +30,7 @@ import (
 	schedulingconfig "github.com/koordinator-sh/koordinator/pkg/scheduler/apis/config"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext/topologymanager"
 	"github.com/koordinator-sh/koordinator/pkg/util"
+	"github.com/koordinator-sh/koordinator/pkg/util/cpuset"
 )
 
 // resourceStrategyTypeMap maps strategy to scorer implementation
@@ -82,7 +83,7 @@ func (p *Plugin) Score(ctx context.Context, cycleState *framework.CycleState, po
 
 	store := topologymanager.GetStore(cycleState)
 	affinity := store.GetAffinity(nodeName)
-	resourceOptions, err := p.getResourceOptions(cycleState, state, node, pod, requestCPUBind, affinity, topologyOptions)
+	resourceOptions, err := p.getResourceOptions(state, node, pod, requestCPUBind, affinity, topologyOptions)
 	if err != nil {
 		return 0, nil
 	}
@@ -91,10 +92,22 @@ func (p *Plugin) Score(ctx context.Context, cycleState *framework.CycleState, po
 		return p.scoreWithAmplifiedCPUs(state, nodeInfo, resourceOptions)
 	}
 
-	podAllocation, err := p.resourceManager.Allocate(node, pod, resourceOptions)
-	if err != nil {
-		return 0, nil
+	reservationRestoreState := getReservationRestoreState(cycleState)
+	restoreState := reservationRestoreState.getNodeState(nodeName)
+	podAllocation, status := p.allocateWithNominatedReservation(restoreState, resourceOptions, pod, node)
+	if !status.IsSuccess() {
+		return 0, status
 	}
+	if podAllocation == nil {
+		resourceOptions.requiredResources = nil
+		resourceOptions.preferredCPUs = cpuset.NewCPUSet()
+		resourceOptions.reusableResources = appendAllocated(nil, restoreState.mergedUnmatchedUsed)
+		podAllocation, status = p.resourceManager.Allocate(node, pod, resourceOptions)
+		if !status.IsSuccess() {
+			return 0, status
+		}
+	}
+
 	allocatable, requested := p.calculateAllocatableAndRequested(node.Name, nodeInfo, podAllocation, resourceOptions)
 	return p.scorer.score(requested, allocatable, framework.NewResource(resourceOptions.requests))
 }
diff --git a/pkg/scheduler/plugins/nodenumaresource/topology_hint.go b/pkg/scheduler/plugins/nodenumaresource/topology_hint.go
index 0f4eb419..348839be 100644
--- a/pkg/scheduler/plugins/nodenumaresource/topology_hint.go
+++ b/pkg/scheduler/plugins/nodenumaresource/topology_hint.go
@@ -25,6 +25,7 @@ import (
 	apiext "github.com/koordinator-sh/koordinator/apis/extension"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext/topologymanager"
+	"github.com/koordinator-sh/koordinator/pkg/util/cpuset"
 )
 
 func (p *Plugin) FilterByNUMANode(ctx context.Context, cycleState *framework.CycleState, pod *corev1.Pod, nodeName string, policyType apiext.NUMATopologyPolicy, exclusivePolicy apiext.NumaTopologyExclusive, topologyOptions TopologyOptions) *framework.Status {
@@ -59,12 +60,14 @@ func (p *Plugin) GetPodTopologyHints(ctx context.Context, cycleState *framework.
 	if !status.IsSuccess() {
 		return nil, status
 	}
-	resourceOptions, err := p.getResourceOptions(cycleState, state, node, pod, requestCPUBind, topologymanager.NUMATopologyHint{}, topologyOptions)
+	reservationRestoreState := getReservationRestoreState(cycleState)
+	restoreState := reservationRestoreState.getNodeState(nodeName)
+	resourceOptions, err := p.getResourceOptions(state, node, pod, requestCPUBind, topologymanager.NUMATopologyHint{}, topologyOptions)
 	if err != nil {
 		return nil, framework.AsStatus(err)
 	}
 	resourceOptions.numaScorer = p.numaScorer
-	hints, err := p.resourceManager.GetTopologyHints(node, pod, resourceOptions, numaTopologyPolicy)
+	hints, err := p.resourceManager.GetTopologyHints(node, pod, resourceOptions, numaTopologyPolicy, restoreState)
 	if err != nil {
 		return nil, framework.NewStatus(framework.Unschedulable, "node(s) Insufficient NUMA Node resources")
 	}
@@ -89,13 +92,26 @@ func (p *Plugin) Allocate(ctx context.Context, cycleState *framework.CycleState,
 		return status
 	}
 
-	resourceOptions, err := p.getResourceOptions(cycleState, state, node, pod, requestCPUBind, affinity, topologyOptions)
+	reservationRestoreState := getReservationRestoreState(cycleState)
+	restoreState := reservationRestoreState.getNodeState(nodeName)
+
+	resourceOptions, err := p.getResourceOptions(state, node, pod, requestCPUBind, affinity, topologyOptions)
 	if err != nil {
 		return framework.NewStatus(framework.UnschedulableAndUnresolvable, err.Error())
 	}
-	_, err = p.resourceManager.Allocate(node, pod, resourceOptions)
-	if err != nil {
-		return framework.NewStatus(framework.Unschedulable, err.Error())
+	podAllocation, status := tryAllocateFromReservation(p.resourceManager, restoreState, resourceOptions, restoreState.matched, pod, node)
+	if !status.IsSuccess() {
+		return status
+	}
+	if podAllocation != nil {
+		return nil
+	}
+	resourceOptions.requiredResources = nil
+	resourceOptions.reusableResources = appendAllocated(nil, restoreState.mergedUnmatchedUsed)
+	resourceOptions.preferredCPUs = cpuset.NewCPUSet()
+	_, status = p.resourceManager.Allocate(node, pod, resourceOptions)
+	if !status.IsSuccess() {
+		return status
 	}
 	return nil
 }
diff --git a/pkg/scheduler/plugins/reservation/plugin.go b/pkg/scheduler/plugins/reservation/plugin.go
index bfd6a396..00365a04 100644
--- a/pkg/scheduler/plugins/reservation/plugin.go
+++ b/pkg/scheduler/plugins/reservation/plugin.go
@@ -21,6 +21,7 @@ import (
 	"fmt"
 	"strconv"
 	"strings"
+	"sync"
 
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/errors"
@@ -148,6 +149,7 @@ func (pl *Plugin) EventsToRegister() []framework.ClusterEventWithHint {
 var _ framework.StateData = &stateData{}
 
 type stateData struct {
+	preemptLock          sync.RWMutex
 	hasAffinity          bool
 	podRequests          corev1.ResourceList
 	podRequestsResources *framework.Resource
@@ -155,7 +157,7 @@ type stateData struct {
 	preemptibleInRRs     map[string]map[types.UID]corev1.ResourceList
 
 	nodeReservationStates    map[string]nodeReservationState
-	nodeReservationDiagnosis map[string]nodeDiagnosisState
+	nodeReservationDiagnosis map[string]*nodeDiagnosisState
 	preferredNode            string
 	assumed                  *frameworkext.ReservationInfo
 }
@@ -174,8 +176,8 @@ type nodeReservationState struct {
 type nodeDiagnosisState struct {
 	nodeName                 string
 	ownerMatched             int // owner matched
-	isUnschedulableUnmatched int // owner matched but unmatched due to unschedulable
-	affinityUnmatched        int // owner matched but unmatched due to affinity
+	isUnschedulableUnmatched int // owner matched but BeforePreFilter unmatched due to unschedulable
+	affinityUnmatched        int // owner matched but BeforePreFilter unmatched due to affinity
 }
 
 func (s *stateData) Clone() framework.StateData {
@@ -188,6 +190,10 @@ func (s *stateData) Clone() framework.StateData {
 		preferredNode:            s.preferredNode,
 		assumed:                  s.assumed,
 	}
+
+	s.preemptLock.RLock()
+	defer s.preemptLock.RUnlock()
+
 	preemptible := map[string]corev1.ResourceList{}
 	for nodeName, returned := range s.preemptible {
 		preemptible[nodeName] = returned.DeepCopy()
@@ -244,6 +250,7 @@ func (pl *Plugin) PreFilter(ctx context.Context, cycleState *framework.CycleStat
 	}
 
 	var preResult *framework.PreFilterResult
+
 	state := getStateData(cycleState)
 	if state.hasAffinity {
 		if len(state.nodeReservationStates) == 0 {
@@ -275,6 +282,9 @@ func (pl *Plugin) AddPod(ctx context.Context, cycleState *framework.CycleState,
 	if rInfo == nil {
 		rInfo = pl.GetNominatedReservation(podInfoToAdd.Pod, node.Name)
 	}
+
+	state.preemptLock.Lock()
+	defer state.preemptLock.Unlock()
 	if rInfo == nil {
 		preemptible := state.preemptible[node.Name]
 		state.preemptible[node.Name] = quotav1.Subtract(preemptible, podRequests)
@@ -303,6 +313,9 @@ func (pl *Plugin) RemovePod(ctx context.Context, cycleState *framework.CycleStat
 	if rInfo == nil {
 		rInfo = pl.GetNominatedReservation(podInfoToRemove.Pod, node.Name)
 	}
+
+	state.preemptLock.Lock()
+	defer state.preemptLock.Unlock()
 	if rInfo == nil {
 		preemptible := state.preemptible[node.Name]
 		state.preemptible[node.Name] = quotav1.Add(preemptible, podRequests)
@@ -368,14 +381,24 @@ func (pl *Plugin) Filter(ctx context.Context, cycleState *framework.CycleState,
 				return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonReservationAffinity)
 			}
 
-			if len(state.preemptible[node.Name]) > 0 || len(state.preemptibleInRRs[node.Name]) > 0 {
-				preemptible := state.preemptible[node.Name]
-				preemptibleResource := framework.NewResource(preemptible)
-				insufficientResources := fitsNode(state.podRequestsResources, nodeInfo, &nodeRState, nil, preemptibleResource)
-				if len(insufficientResources) != 0 {
-					return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonPreemptionFailed)
+			status := func() *framework.Status {
+				state.preemptLock.RLock()
+				defer state.preemptLock.RUnlock()
+
+				if len(state.preemptible[node.Name]) > 0 || len(state.preemptibleInRRs[node.Name]) > 0 {
+					preemptible := state.preemptible[node.Name]
+					preemptibleResource := framework.NewResource(preemptible)
+					insufficientResources := fitsNode(state.podRequestsResources, nodeInfo, &nodeRState, nil, preemptibleResource)
+					if len(insufficientResources) != 0 {
+						return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonPreemptionFailed)
+					}
 				}
+				return nil
+			}()
+			if !status.IsSuccess() {
+				return status
 			}
+
 			return nil
 		}
 
@@ -401,10 +424,13 @@ func (pl *Plugin) filterWithReservations(ctx context.Context, cycleState *framew
 			continue
 		}
 
+		state.preemptLock.RLock()
 		preemptibleInRR := state.preemptibleInRRs[node.Name][rInfo.UID()]
 		preemptible := framework.NewResource(preemptibleInRR)
 		preemptible.Add(state.preemptible[node.Name])
 		insufficientResourcesByNode := fitsNode(state.podRequestsResources, nodeInfo, &nodeRState, rInfo, preemptible)
+		state.preemptLock.RUnlock()
+
 		nodeFits := len(insufficientResourcesByNode) == 0
 		allocatePolicy := rInfo.GetAllocatePolicy()
 		if allocatePolicy == schedulingv1alpha1.ReservationAllocatePolicyDefault ||
@@ -441,7 +467,7 @@ func (pl *Plugin) filterWithReservations(ctx context.Context, cycleState *framew
 			failureReasons = append(failureReasons, fmt.Sprintf("Insufficient %s by node", insufficientResourceByNode))
 		}
 		for _, insufficientResourceByReservation := range allInsufficientResourcesByReservation {
-			failureReasons = append(failureReasons, fmt.Sprintf("Reservation(s) Insufficient %s", insufficientResourceByReservation))
+			failureReasons = append(failureReasons, reservationutil.NewReservationReason("Insufficient "+insufficientResourceByReservation))
 		}
 		if len(failureReasons) == 0 {
 			failureReasons = append(failureReasons, ErrReasonNoReservationsMeetRequirements)
@@ -507,20 +533,44 @@ func fitsNode(podRequest *framework.Resource, nodeInfo *framework.NodeInfo, node
 	return insufficientResources
 }
 
-func (pl *Plugin) PostFilter(ctx context.Context, cycleState *framework.CycleState, pod *corev1.Pod, _ framework.NodeToStatusMap) (*framework.PostFilterResult, *framework.Status) {
-	// Implement an empty function to be compatible with existing configurations
+func (pl *Plugin) PostFilter(_ context.Context, cycleState *framework.CycleState, _ *corev1.Pod, filteredNodeStatusMap framework.NodeToStatusMap) (*framework.PostFilterResult, *framework.Status) {
 	state := getStateData(cycleState)
-	reasons := pl.makePostFilterReasons(state)
+	reasons := pl.makePostFilterReasons(state, filteredNodeStatusMap)
 	return nil, framework.NewStatus(framework.Unschedulable, reasons...)
 }
 
-func (pl *Plugin) makePostFilterReasons(state *stateData) []string {
+func (pl *Plugin) makePostFilterReasons(state *stateData, filteredNodeStatusMap framework.NodeToStatusMap) []string {
 	ownerMatched, affinityUnmatched, isUnSchedulableUnmatched := 0, 0, 0
-	for _, nodeState := range state.nodeReservationDiagnosis {
-		isUnSchedulableUnmatched += nodeState.isUnschedulableUnmatched
-		affinityUnmatched += nodeState.affinityUnmatched
-		ownerMatched += nodeState.ownerMatched
+	// failure reasons and counts for the nodes which have not been handled by the Reservation's Filter
+	reasonsByNode := map[string]int{}
+	for nodeName, diagnosisState := range state.nodeReservationDiagnosis {
+		isUnSchedulableUnmatched += diagnosisState.isUnschedulableUnmatched
+		affinityUnmatched += diagnosisState.affinityUnmatched
+		ownerMatched += diagnosisState.ownerMatched
+		// calculate the remaining unmatched which is owner-matched and Reservation BeforePreFilter matched
+		remainUnmatched := diagnosisState.ownerMatched - diagnosisState.affinityUnmatched - diagnosisState.isUnschedulableUnmatched
+		if remainUnmatched <= 0 { // no need to check other reasons
+			continue
+		}
+		// count the failure reasons which is neither counted by the PreFilterTransformer nor by the Reservation Filter.
+		nodeReasons := map[string]int{}
+		if failureStatus, ok := filteredNodeStatusMap[nodeName]; ok {
+			for _, reason := range failureStatus.Reasons() {
+				if reservationutil.IsReservationReason(reason) { // reservation-level reasons are not counted
+					remainUnmatched--
+					continue
+				}
+				nodeReasons[reason]++
+			}
+		}
+		if remainUnmatched <= 0 { // capped with zero
+			continue
+		}
+		for reason, count := range nodeReasons {
+			reasonsByNode[reason] += remainUnmatched * count
+		}
 	}
+	// if the pod specifies an affinity, we always show the owner matched reason
 	if ownerMatched <= 0 && !state.hasAffinity {
 		return nil
 	}
@@ -541,6 +591,13 @@ func (pl *Plugin) makePostFilterReasons(state *stateData) []string {
 		reasons = append(reasons, b.String())
 		b.Reset()
 	}
+	for nodeReason, count := range reasonsByNode { // node reason Filter failed
+		b.WriteString(strconv.Itoa(count))
+		b.WriteString(" Reservation(s) for node reason that ")
+		b.WriteString(nodeReason)
+		reasons = append(reasons, b.String())
+		b.Reset()
+	}
 	b.WriteString(strconv.Itoa(ownerMatched))
 	b.WriteString(" Reservation(s) matched owner total")
 	reasons = append(reasons, b.String())
diff --git a/pkg/scheduler/plugins/reservation/transformer.go b/pkg/scheduler/plugins/reservation/transformer.go
index 6b3264f1..090ca29c 100644
--- a/pkg/scheduler/plugins/reservation/transformer.go
+++ b/pkg/scheduler/plugins/reservation/transformer.go
@@ -234,7 +234,7 @@ func (pl *Plugin) prepareMatchReservationState(ctx context.Context, cycleState *
 		preemptible:              map[string]corev1.ResourceList{},
 		preemptibleInRRs:         map[string]map[types.UID]corev1.ResourceList{},
 		nodeReservationStates:    map[string]nodeReservationState{},
-		nodeReservationDiagnosis: map[string]nodeDiagnosisState{},
+		nodeReservationDiagnosis: map[string]*nodeDiagnosisState{},
 	}
 	pluginToNodeReservationRestoreState := frameworkext.PluginToNodeReservationRestoreStates{}
 	for index := range allNodeReservationStates {
@@ -254,7 +254,7 @@ func (pl *Plugin) prepareMatchReservationState(ctx context.Context, cycleState *
 	}
 	for i := range allNodeDiagnosisStates {
 		v := allNodeDiagnosisStates[i]
-		state.nodeReservationDiagnosis[v.nodeName] = *v
+		state.nodeReservationDiagnosis[v.nodeName] = v
 	}
 	if extender != nil {
 		status := extender.RunReservationExtensionFinalRestoreReservation(ctx, cycleState, pod, pluginToNodeReservationRestoreState)
diff --git a/pkg/util/reservation/reservation.go b/pkg/util/reservation/reservation.go
index e48bb5aa..b4628c79 100644
--- a/pkg/util/reservation/reservation.go
+++ b/pkg/util/reservation/reservation.go
@@ -21,6 +21,7 @@ import (
 	"fmt"
 	"math"
 	"strconv"
+	"strings"
 
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -50,6 +51,9 @@ var (
 	AnnotationReservationResizeAllocatable = extension.SchedulingDomainPrefix + "/reservation-resize-allocatable"
 )
 
+// ErrReasonPrefix is the prefix of the reservation-level scheduling errors.
+const ErrReasonPrefix = "Reservation(s) "
+
 // NewReservePod returns a fake pod set as the reservation's specifications.
 // The reserve pod is only visible for the scheduler and does not make actual creation on nodes.
 func NewReservePod(r *schedulingv1alpha1.Reservation) *corev1.Pod {
@@ -560,3 +564,13 @@ func GetReservationRestrictedResources(allocatableResources []corev1.ResourceNam
 	}
 	return result
 }
+
+// NewReservationReason creates a reservation-level error reason with the given message.
+func NewReservationReason(msg string) string {
+	return ErrReasonPrefix + msg
+}
+
+// IsReservationReason checks if the error reason is at the reservation-level.
+func IsReservationReason(reason string) bool {
+	return strings.HasPrefix(reason, ErrReasonPrefix)
+}



Test patch:
diff --git a/pkg/scheduler/frameworkext/eventhandlers/reservation_handler_test.go b/pkg/scheduler/frameworkext/eventhandlers/reservation_handler_test.go
index c1a836d4..155ca8da 100644
--- a/pkg/scheduler/frameworkext/eventhandlers/reservation_handler_test.go
+++ b/pkg/scheduler/frameworkext/eventhandlers/reservation_handler_test.go
@@ -1344,46 +1344,56 @@ func Test_generatePodEventOnReservationLevel(t *testing.T) {
 		},
 		{
 			name: "pod topology spread constraints missing required label errors",
-			errorMsg: "0/5 nodes are available: 3 node(s) didn't match pod topology spread constraints (missing required label)," +
-				"1 Insufficient cpu, 1 Insufficient memory, 2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
+			errorMsg: "0/5 nodes are available: 3 node(s) didn't match pod topology spread constraints (missing required label), " +
+				"1 Insufficient cpu, 1 Insufficient memory, " +
+				"3 Reservation(s) for node reason that didn't match pod topology spread constraints (missing required label), " +
+				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory, " +
 				"8 Reservation(s) matched owner total.",
-			wantMsg: "0/8 reservations are available: at least 3 didn't match pod topology spread constraints (missing required label) Reservation(s), " +
+			wantMsg: "0/8 reservations are available: 3 Reservation(s) for node reason that didn't match pod topology spread constraints (missing required label), " +
 				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory.",
 			wantIsReserve: true,
 		},
 		{
 			name: "pod topology spread constraints errors",
 			errorMsg: "0/5 nodes are available: 3 node(s) didn't match pod topology spread constraints," +
-				"1 Insufficient cpu, 1 Insufficient memory, 2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
+				"1 Insufficient cpu, 1 Insufficient memory, " +
+				"3 Reservation(s) for node reason that didn't match pod topology spread constraints," +
+				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
 				"8 Reservation(s) matched owner total.",
-			wantMsg: "0/8 reservations are available: at least 3 didn't match pod topology spread constraints Reservation(s), " +
+			wantMsg: "0/8 reservations are available: 3 Reservation(s) for node reason that didn't match pod topology spread constraints, " +
 				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory.",
 			wantIsReserve: true,
 		},
 		{
 			name: "satisfy existing pods anti-affinity rules, errors",
 			errorMsg: "0/5 nodes are available: 3 node(s) didn't satisfy existing pods anti-affinity rules," +
-				"1 Insufficient cpu, 1 Insufficient memory, 2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
+				"1 Insufficient cpu, 1 Insufficient memory, " +
+				"3 Reservation(s) for node reason that didn't satisfy existing pods anti-affinity rules," +
+				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
 				"8 Reservation(s) matched owner total.",
-			wantMsg: "0/8 reservations are available: at least 3 didn't satisfy existing pods anti-affinity rules Reservation(s), " +
+			wantMsg: "0/8 reservations are available: 3 Reservation(s) for node reason that didn't satisfy existing pods anti-affinity rules, " +
 				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory.",
 			wantIsReserve: true,
 		},
 		{
 			name: "match pod affinity rules errors",
 			errorMsg: "0/5 nodes are available: 3 node(s) didn't match pod affinity rules," +
-				"1 Insufficient cpu, 1 Insufficient memory, 2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
+				"1 Insufficient cpu, 1 Insufficient memory, " +
+				"3 Reservation(s) for node reason that didn't match pod affinity rules, " +
+				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
 				"8 Reservation(s) matched owner total.",
-			wantMsg: "0/8 reservations are available: at least 3 didn't match pod affinity rules Reservation(s), " +
+			wantMsg: "0/8 reservations are available: 3 Reservation(s) for node reason that didn't match pod affinity rules, " +
 				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory.",
 			wantIsReserve: true,
 		},
 		{
 			name: "match pod anti-affinity rules errors",
 			errorMsg: "0/5 nodes are available: 3 node(s) didn't match pod anti-affinity rules," +
-				"1 Insufficient cpu, 1 Insufficient memory, 2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
+				"1 Insufficient cpu, 1 Insufficient memory, " +
+				"3 Reservation(s) for node reason that didn't match pod anti-affinity rules, " +
+				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
 				"8 Reservation(s) matched owner total.",
-			wantMsg: "0/8 reservations are available: at least 3 didn't match pod anti-affinity rules Reservation(s), " +
+			wantMsg: "0/8 reservations are available: 3 Reservation(s) for node reason that didn't match pod anti-affinity rules, " +
 				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory.",
 			wantIsReserve: true,
 		},
@@ -1391,13 +1401,29 @@ func Test_generatePodEventOnReservationLevel(t *testing.T) {
 			name: "mix affinity errors of 'match pod topology spread constraints' and 'match pod affinity rules'",
 			errorMsg: "0/5 nodes are available: 3 node(s) didn't match pod topology spread constraints, " +
 				"1 node(s) didn't match pod affinity rules, " +
-				"1 Insufficient memory, 2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
+				"1 Insufficient memory, " +
+				"3 Reservation(s) for node reason that didn't match pod topology spread constraints, " +
+				"1 Reservation(s) for node reason that didn't match pod affinity rules, " +
+				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
 				"8 Reservation(s) matched owner total.",
-			wantMsg: "0/8 reservations are available: at least 3 didn't match pod topology spread constraints Reservation(s), " +
-				"at least 1 didn't match pod affinity rules Reservation(s), " +
+			wantMsg: "0/8 reservations are available: 3 Reservation(s) for node reason that didn't match pod topology spread constraints, " +
+				"1 Reservation(s) for node reason that didn't match pod affinity rules, " +
 				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory.",
 			wantIsReserve: true,
 		},
+		{
+			name: "reservation of node reason",
+			errorMsg: "0/5 nodes are available: 2 node(s) didn't match pod topology spread constraints, " +
+				"1 node(s) didn't match pod affinity rules, 1 Insufficient memory, " +
+				"3 Reservation(s) for node reason that didn't match pod topology spread constraints, " +
+				"2 Reservation(s) for node reason that didn't match pod affinity rules," +
+				"2 Reservation(s) Insufficient cpu, 1 Reservation(s) Insufficient memory. " +
+				"8 Reservation(s) matched owner total.",
+			wantMsg: "0/8 reservations are available: 3 Reservation(s) for node reason that didn't match pod topology spread constraints, " +
+				"2 Reservation(s) for node reason that didn't match pod affinity rules, 2 Reservation(s) Insufficient cpu, " +
+				"1 Reservation(s) Insufficient memory.",
+			wantIsReserve: true,
+		},
 		{
 			name: "only gang errors",
 			errorMsg: "Gang \"default/demo-job-podgroup\" gets rejected due to member Pod \"demo-job-kfqfs\" is" +
diff --git a/pkg/scheduler/plugins/elasticquota/pod_handler_test.go b/pkg/scheduler/plugins/elasticquota/pod_handler_test.go
index 5c147a68..0c2bb63a 100644
--- a/pkg/scheduler/plugins/elasticquota/pod_handler_test.go
+++ b/pkg/scheduler/plugins/elasticquota/pod_handler_test.go
@@ -28,6 +28,7 @@ import (
 
 	"github.com/koordinator-sh/koordinator/apis/extension"
 	koordfeatures "github.com/koordinator-sh/koordinator/pkg/features"
+	"github.com/koordinator-sh/koordinator/pkg/scheduler/plugins/elasticquota/core"
 	utilfeature "github.com/koordinator-sh/koordinator/pkg/util/feature"
 )
 
@@ -184,9 +185,14 @@ func TestPlugin_OnPodDelete(t *testing.T) {
 
 	suit.AddQuota("test", "", 10, 40, 10, 40, 10, 40, false, "")
 
-	testQuotaInfo := plugin.groupQuotaManager.GetQuotaInfoByName("test")
-
-	assert.True(t, quotav1.Equals(testQuotaInfo.GetUsed(), v1.ResourceList{}))
+	var testQuotaInfo *core.QuotaInfo
+	for {
+		testQuotaInfo = plugin.groupQuotaManager.GetQuotaInfoByName("test")
+		if testQuotaInfo != nil {
+			assert.True(t, quotav1.Equals(testQuotaInfo.GetUsed(), v1.ResourceList{}))
+			break
+		}
+	}
 
 	pod := makePod2("pod", MakeResourceList().CPU(1).Mem(2).Obj())
 	pod.Labels[extension.LabelQuotaName] = "test"
diff --git a/pkg/scheduler/plugins/nodenumaresource/plugin_test.go b/pkg/scheduler/plugins/nodenumaresource/plugin_test.go
index b4672b0f..896e03c3 100644
--- a/pkg/scheduler/plugins/nodenumaresource/plugin_test.go
+++ b/pkg/scheduler/plugins/nodenumaresource/plugin_test.go
@@ -31,6 +31,7 @@ import (
 	apiruntime "k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/uuid"
+	quotav1 "k8s.io/apiserver/pkg/quota/v1"
 	"k8s.io/client-go/informers"
 	kubefake "k8s.io/client-go/kubernetes/fake"
 	apiresource "k8s.io/kubernetes/pkg/api/v1/resource"
@@ -1022,16 +1023,21 @@ func TestFilterWithAmplifiedCPUs(t *testing.T) {
 }
 
 func TestPlugin_Reserve(t *testing.T) {
+	node0, _ := bitmask.NewBitMask(0)
+	node1, _ := bitmask.NewBitMask(1)
 	tests := []struct {
-		name          string
-		nodeLabels    map[string]string
-		state         *preFilterState
-		reservedCPUs  map[types.UID]cpuset.CPUSet
-		pod           *corev1.Pod
-		cpuTopology   *CPUTopology
-		allocatedCPUs []int
-		want          *framework.Status
-		wantCPUSet    cpuset.CPUSet
+		name                      string
+		nodeLabels                map[string]string
+		state                     *preFilterState
+		matched                   map[types.UID]reservationAlloc
+		reservationAllocatePolicy schedulingv1alpha1.ReservationAllocatePolicy
+		pod                       *corev1.Pod
+		numaAffinity              bitmask.BitMask
+		cpuTopology               *CPUTopology
+		allocatedCPUs             []int
+		want                      *framework.Status
+		wantCPUSet                cpuset.CPUSet
+		wantNUMAResource          []NUMANodeResource
 	}{
 		{
 			name: "error with missing preFilterState",
@@ -1099,7 +1105,6 @@ func TestPlugin_Reserve(t *testing.T) {
 			cpuTopology: buildCPUTopologyForTest(2, 1, 4, 2),
 			pod:         &corev1.Pod{},
 			want:        nil,
-			wantCPUSet:  cpuset.NewCPUSet(),
 		},
 		{
 			name: "error with big request cpu",
@@ -1144,19 +1149,190 @@ func TestPlugin_Reserve(t *testing.T) {
 			wantCPUSet:    cpuset.NewCPUSet(4, 5, 6, 7),
 		},
 		{
-			name: "succeed allocate from reservation reserved cpus",
+			name: "succeed allocate from reservation reserved cpus;default allocate policy",
 			state: &preFilterState{
 				requestCPUBind:         true,
 				numCPUsNeeded:          4,
 				preferredCPUBindPolicy: schedulingconfig.CPUBindPolicyFullPCPUs,
 			},
-			reservedCPUs: map[types.UID]cpuset.CPUSet{
-				uuid.NewUUID(): cpuset.NewCPUSet(4, 5, 6, 7, 8, 9, 10),
+			matched: map[types.UID]reservationAlloc{
+				uuid.NewUUID(): {
+					reservedCPUs: cpuset.NewCPUSet(4, 5, 6, 7, 8, 9, 10),
+				},
 			},
-			cpuTopology: buildCPUTopologyForTest(2, 1, 4, 2),
-			pod:         &corev1.Pod{},
-			want:        nil,
-			wantCPUSet:  cpuset.NewCPUSet(4, 5, 6, 7),
+			reservationAllocatePolicy: schedulingv1alpha1.ReservationAllocatePolicyDefault,
+			cpuTopology:               buildCPUTopologyForTest(2, 1, 4, 2),
+			pod:                       &corev1.Pod{},
+			want:                      nil,
+			wantCPUSet:                cpuset.NewCPUSet(4, 5, 6, 7),
+		},
+		{
+			name: "succeed allocate from reservation reserved cpus;restricted allocate policy",
+			state: &preFilterState{
+				requestCPUBind:         true,
+				numCPUsNeeded:          4,
+				preferredCPUBindPolicy: schedulingconfig.CPUBindPolicyFullPCPUs,
+			},
+			matched: map[types.UID]reservationAlloc{
+				uuid.NewUUID(): {
+					reservedCPUs: cpuset.NewCPUSet(4, 5, 6, 7, 8, 9, 10),
+				},
+			},
+			reservationAllocatePolicy: schedulingv1alpha1.ReservationAllocatePolicyRestricted,
+			cpuTopology:               buildCPUTopologyForTest(2, 1, 4, 2),
+			pod:                       &corev1.Pod{},
+			want:                      nil,
+			wantCPUSet:                cpuset.NewCPUSet(4, 5, 6, 7),
+		},
+		{
+			name: "failed to allocate from reservation reserved cpus;restricted allocate policy",
+			state: &preFilterState{
+				requestCPUBind:         true,
+				numCPUsNeeded:          4,
+				preferredCPUBindPolicy: schedulingconfig.CPUBindPolicyFullPCPUs,
+				hasReservationAffinity: true,
+			},
+			matched: map[types.UID]reservationAlloc{
+				uuid.NewUUID(): {
+					reservedCPUs: cpuset.NewCPUSet(4, 5),
+				},
+			},
+			reservationAllocatePolicy: schedulingv1alpha1.ReservationAllocatePolicyRestricted,
+			cpuTopology:               buildCPUTopologyForTest(2, 1, 4, 2),
+			pod:                       &corev1.Pod{},
+			want:                      framework.NewStatus(framework.Unschedulable, "Reservation(s) not enough cpus available to satisfy request"),
+			wantCPUSet:                cpuset.NewCPUSet(),
+		},
+		{
+			name: "succeed allocate from reservation reserved numa resource and cpuset;restricted allocate policy",
+			state: &preFilterState{
+				requestCPUBind:         true,
+				numCPUsNeeded:          4,
+				requests:               corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("4")},
+				preferredCPUBindPolicy: schedulingconfig.CPUBindPolicyFullPCPUs,
+				podNUMATopologyPolicy:  extension.NUMATopologyPolicyRestricted,
+				hasReservationAffinity: true,
+			},
+			matched: map[types.UID]reservationAlloc{
+				uuid.NewUUID(): {
+					reservedCPUs: cpuset.NewCPUSet(4, 5, 6, 7, 8, 9, 10),
+					allocatable: map[int]corev1.ResourceList{
+						0: {
+							corev1.ResourceCPU: resource.MustParse("7"),
+						},
+					},
+				},
+			},
+			reservationAllocatePolicy: schedulingv1alpha1.ReservationAllocatePolicyRestricted,
+			cpuTopology:               buildCPUTopologyForTest(2, 1, 8, 2),
+			pod:                       &corev1.Pod{},
+			numaAffinity:              node0,
+			want:                      nil,
+			wantCPUSet:                cpuset.NewCPUSet(4, 5, 6, 7),
+			wantNUMAResource: []NUMANodeResource{
+				{
+					Node:      0,
+					Resources: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("4")},
+				},
+			},
+		},
+		{
+			name: "failed to allocate from reservation reserved numa resource and cpuset;restricted allocate policy",
+			state: &preFilterState{
+				requestCPUBind:         true,
+				numCPUsNeeded:          4,
+				requests:               corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("4")},
+				preferredCPUBindPolicy: schedulingconfig.CPUBindPolicyFullPCPUs,
+				podNUMATopologyPolicy:  extension.NUMATopologyPolicyRestricted,
+				hasReservationAffinity: true,
+			},
+			matched: map[types.UID]reservationAlloc{
+				uuid.NewUUID(): {
+					reservedCPUs: cpuset.NewCPUSet(4, 5),
+					allocatable: map[int]corev1.ResourceList{
+						0: {
+							corev1.ResourceCPU: resource.MustParse("2"),
+						},
+					},
+					remained: map[int]corev1.ResourceList{
+						0: {
+							corev1.ResourceCPU: resource.MustParse("2"),
+						},
+					},
+				},
+			},
+			reservationAllocatePolicy: schedulingv1alpha1.ReservationAllocatePolicyRestricted,
+			cpuTopology:               buildCPUTopologyForTest(2, 1, 4, 2),
+			pod:                       &corev1.Pod{},
+			numaAffinity:              node0,
+			want:                      framework.NewStatus(framework.Unschedulable, "Reservation(s) not enough cpus available to satisfy request"),
+			wantCPUSet:                cpuset.NewCPUSet(),
+		},
+		{
+			name: "succeed allocate from reservation reserved numa resource;restricted allocate policy",
+			state: &preFilterState{
+				numCPUsNeeded:          4,
+				requests:               corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("4")},
+				preferredCPUBindPolicy: schedulingconfig.CPUBindPolicyFullPCPUs,
+				podNUMATopologyPolicy:  extension.NUMATopologyPolicyRestricted,
+				hasReservationAffinity: true,
+			},
+			matched: map[types.UID]reservationAlloc{
+				uuid.NewUUID(): {
+					allocatable: map[int]corev1.ResourceList{
+						1: {
+							corev1.ResourceCPU: resource.MustParse("7"),
+						},
+					},
+					remained: map[int]corev1.ResourceList{
+						1: {
+							corev1.ResourceCPU: resource.MustParse("7"),
+						},
+					},
+				},
+			},
+			reservationAllocatePolicy: schedulingv1alpha1.ReservationAllocatePolicyRestricted,
+			cpuTopology:               buildCPUTopologyForTest(2, 1, 4, 2),
+			pod:                       &corev1.Pod{},
+			numaAffinity:              node1,
+			want:                      nil,
+			wantCPUSet:                cpuset.CPUSet{},
+			wantNUMAResource: []NUMANodeResource{
+				{
+					Node:      1,
+					Resources: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("4")},
+				},
+			},
+		},
+		{
+			name: "failed to allocate from reservation reserved numa resource;restricted allocate policy",
+			state: &preFilterState{
+				numCPUsNeeded:          4,
+				requests:               corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("4")},
+				podNUMATopologyPolicy:  extension.NUMATopologyPolicyRestricted,
+				preferredCPUBindPolicy: schedulingconfig.CPUBindPolicyFullPCPUs,
+				hasReservationAffinity: true,
+			},
+			matched: map[types.UID]reservationAlloc{
+				uuid.NewUUID(): {
+					allocatable: map[int]corev1.ResourceList{
+						0: {
+							corev1.ResourceCPU: resource.MustParse("2"),
+						},
+					},
+					remained: map[int]corev1.ResourceList{
+						0: {
+							corev1.ResourceCPU: resource.MustParse("2"),
+						},
+					},
+				},
+			},
+			reservationAllocatePolicy: schedulingv1alpha1.ReservationAllocatePolicyRestricted,
+			cpuTopology:               buildCPUTopologyForTest(2, 1, 4, 2),
+			pod:                       &corev1.Pod{},
+			numaAffinity:              node0,
+			want:                      framework.NewStatus(framework.Unschedulable, "Reservation(s) Insufficient NUMA cpu"),
+			wantCPUSet:                cpuset.NewCPUSet(),
 		},
 	}
 	for _, tt := range tests {
@@ -1189,13 +1365,32 @@ func TestPlugin_Reserve(t *testing.T) {
 			if tt.cpuTopology != nil {
 				plg.topologyOptionsManager.UpdateTopologyOptions(nodeAllocation.nodeName, func(options *TopologyOptions) {
 					options.CPUTopology = tt.cpuTopology
+					for i := 0; i < tt.cpuTopology.NumSockets; i++ {
+						options.NUMANodeResources = append(options.NUMANodeResources, NUMANodeResource{
+							Node:      i,
+							Resources: corev1.ResourceList{corev1.ResourceCPU: *resource.NewQuantity(int64(tt.cpuTopology.CPUsPerNode()), resource.DecimalSI)},
+						})
+					}
 				})
 				if len(tt.allocatedCPUs) > 0 {
 					nodeAllocation.addCPUs(tt.cpuTopology, uuid.NewUUID(), cpuset.NewCPUSet(tt.allocatedCPUs...), schedulingconfig.CPUExclusivePolicyNone)
 				}
-				if len(tt.reservedCPUs) > 0 {
-					for reservationUID, cpus := range tt.reservedCPUs {
-						nodeAllocation.addCPUs(tt.cpuTopology, reservationUID, cpus, schedulingconfig.CPUExclusivePolicyNone)
+				if len(tt.matched) > 0 {
+					for reservationUID, alloc := range tt.matched {
+						nodeAllocation.addCPUs(tt.cpuTopology, reservationUID, alloc.reservedCPUs, schedulingconfig.CPUExclusivePolicyNone)
+						var numaResources []NUMANodeResource
+						for i, allocatable := range alloc.allocatable {
+							numaResources = append(numaResources, NUMANodeResource{
+								Node:      i,
+								Resources: allocatable,
+							})
+						}
+						nodeAllocation.addPodAllocation(&PodAllocation{
+							UID:                reservationUID,
+							CPUSet:             alloc.reservedCPUs,
+							CPUExclusivePolicy: schedulingconfig.CPUExclusivePolicyNone,
+							NUMANodeResources:  numaResources,
+						}, tt.cpuTopology)
 					}
 				}
 			}
@@ -1208,26 +1403,36 @@ func TestPlugin_Reserve(t *testing.T) {
 			cycleState := framework.NewCycleState()
 			if tt.state != nil {
 				cycleState.Write(stateKey, tt.state)
-				if len(tt.reservedCPUs) > 0 {
-					for reservationUID := range tt.reservedCPUs {
+				if len(tt.matched) > 0 {
+					for reservationUID, alloc := range tt.matched {
 						reservation := &schedulingv1alpha1.Reservation{
 							ObjectMeta: metav1.ObjectMeta{
 								UID:  reservationUID,
 								Name: "test-reservation",
 							},
+							Spec: schedulingv1alpha1.ReservationSpec{
+								AllocatePolicy: tt.reservationAllocatePolicy,
+							},
 						}
+						alloc.rInfo = frameworkext.NewReservationInfo(reservation)
+						tt.matched[reservationUID] = alloc
 						rInfo := frameworkext.NewReservationInfo(reservation)
 						plg.handle.GetReservationNominator().AddNominatedReservation(tt.pod, "test-node-1", rInfo)
 					}
 					cycleState.Write(reservationRestoreStateKey, &reservationRestoreStateData{
-						skip: false,
 						nodeToState: frameworkext.NodeReservationRestoreStates{
-							"test-node-1": &nodeReservationRestoreStateData{reservedCPUs: tt.reservedCPUs},
+							"test-node-1": &nodeReservationRestoreStateData{matched: tt.matched},
 						},
 					})
 				}
 			}
 
+			if tt.numaAffinity != nil {
+				topologymanager.InitStore(cycleState)
+				store := topologymanager.GetStore(cycleState)
+				store.SetAffinity("test-node-1", topologymanager.NUMATopologyHint{NUMANodeAffinity: tt.numaAffinity})
+			}
+
 			nodeInfo, err := suit.Handle.SnapshotSharedLister().NodeInfos().Get("test-node-1")
 			assert.NoError(t, err)
 			assert.NotNil(t, nodeInfo)
@@ -1239,10 +1444,16 @@ func TestPlugin_Reserve(t *testing.T) {
 				return
 			}
 			var gotCPUs cpuset.CPUSet
+			var gotNUMAResource []NUMANodeResource
 			if tt.state.allocation != nil {
 				gotCPUs = tt.state.allocation.CPUSet
+				gotNUMAResource = tt.state.allocation.NUMANodeResources
+			}
+			assert.Equal(t, tt.wantCPUSet, gotCPUs)
+			for i, nodeResource := range gotNUMAResource {
+				assert.Equal(t, tt.wantNUMAResource[i].Node, nodeResource.Node)
+				assert.True(t, quotav1.Equals(tt.wantNUMAResource[i].Resources, nodeResource.Resources))
 			}
-			assert.True(t, tt.wantCPUSet.Equals(gotCPUs))
 		})
 	}
 }
@@ -1536,7 +1747,7 @@ func TestRestoreReservation(t *testing.T) {
 
 	nodeReservationState, status := pl.RestoreReservation(context.TODO(), cycleState, testPod, []*frameworkext.ReservationInfo{rInfo}, nil, nodeInfo)
 	assert.True(t, status.IsSuccess())
-	assert.Equal(t, cpuset.NewCPUSet(8, 9), nodeReservationState.(*nodeReservationRestoreStateData).reservedCPUs[reservation.UID])
+	assert.Equal(t, cpuset.NewCPUSet(8, 9), nodeReservationState.(*nodeReservationRestoreStateData).matched[reservation.UID].reservedCPUs)
 
 	rInfo.AddAssignedPod(podB)
 	nodeReservationState, status = pl.RestoreReservation(context.TODO(), cycleState, testPod, []*frameworkext.ReservationInfo{rInfo}, nil, nodeInfo)
diff --git a/pkg/scheduler/plugins/nodenumaresource/resource_manager_test.go b/pkg/scheduler/plugins/nodenumaresource/resource_manager_test.go
index a37a7d5e..51fed483 100644
--- a/pkg/scheduler/plugins/nodenumaresource/resource_manager_test.go
+++ b/pkg/scheduler/plugins/nodenumaresource/resource_manager_test.go
@@ -24,7 +24,6 @@ import (
 	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	quotav1 "k8s.io/apiserver/pkg/quota/v1"
-	"k8s.io/klog/v2"
 
 	apiext "github.com/koordinator-sh/koordinator/apis/extension"
 	schedulingconfig "github.com/koordinator-sh/koordinator/pkg/scheduler/apis/config"
@@ -1613,29 +1612,7 @@ func TestResourceManagerGetTopologyHint(t *testing.T) {
 				},
 			},
 			want: map[string][]topologymanager.NUMATopologyHint{
-				string(corev1.ResourceCPU): {
-					{
-						NUMANodeAffinity: func() bitmask.BitMask {
-							mask, _ := bitmask.NewBitMask(0)
-							return mask
-						}(),
-						Preferred: true,
-					},
-					{
-						NUMANodeAffinity: func() bitmask.BitMask {
-							mask, _ := bitmask.NewBitMask(1)
-							return mask
-						}(),
-						Preferred: true,
-					},
-					{
-						NUMANodeAffinity: func() bitmask.BitMask {
-							mask, _ := bitmask.NewBitMask(0, 1)
-							return mask
-						}(),
-						Preferred: false,
-					},
-				},
+				string(corev1.ResourceCPU):             {},
 				string(corev1.ResourceMemory):          {},
 				corev1.ResourceHugePagesPrefix + "1Gi": {},
 			},
@@ -1692,7 +1669,7 @@ func TestResourceManagerGetTopologyHint(t *testing.T) {
 			}
 			assert.NoError(t, amplifyNUMANodeResources(node, &tt.options.topologyOptions))
 
-			got, err := resourceManager.GetTopologyHints(node, tt.pod, tt.options, apiext.NUMATopologyPolicyBestEffort)
+			got, err := resourceManager.GetTopologyHints(node, tt.pod, tt.options, apiext.NUMATopologyPolicyBestEffort, &nodeReservationRestoreStateData{})
 			if tt.wantErr != (err != nil) {
 				t.Errorf("wantErr %v but got %v", tt.wantErr, err != nil)
 			}
@@ -1700,9 +1677,3 @@ func TestResourceManagerGetTopologyHint(t *testing.T) {
 		})
 	}
 }
-
-func TestA(t *testing.T) {
-	a := map[string]corev1.ResourceList{}
-	b := a["a"]
-	klog.Info(b["cpu"])
-}
diff --git a/pkg/scheduler/plugins/reservation/plugin_test.go b/pkg/scheduler/plugins/reservation/plugin_test.go
index bbab4145..66c4a7d4 100644
--- a/pkg/scheduler/plugins/reservation/plugin_test.go
+++ b/pkg/scheduler/plugins/reservation/plugin_test.go
@@ -1834,10 +1834,18 @@ func TestFilterReservation(t *testing.T) {
 }
 
 func TestPostFilter(t *testing.T) {
+	testFilterStatus := framework.NewStatus(framework.Unschedulable, "node(s) didn't match the requested node name")
+	testFilterReservationStatus := framework.NewStatus(framework.Unschedulable,
+		reservationutil.NewReservationReason("Insufficient nvidia.com/gpu"),
+		reservationutil.NewReservationReason("Insufficient koordinator.sh/gpu-mem-ratio"))
+	testFilterReservationStatus1 := framework.NewStatus(framework.Unschedulable,
+		reservationutil.NewReservationReason("Insufficient cpu"),
+		"Insufficient memory")
 	type args struct {
 		hasStateData             bool
 		hasAffinity              bool
-		nodeReservationDiagnosis map[string]nodeDiagnosisState
+		nodeReservationDiagnosis map[string]*nodeDiagnosisState
+		filteredNodeStatusMap    framework.NodeToStatusMap
 	}
 	tests := []struct {
 		name  string
@@ -1848,7 +1856,8 @@ func TestPostFilter(t *testing.T) {
 		{
 			name: "no reservation filtering",
 			args: args{
-				hasStateData: false,
+				hasStateData:          false,
+				filteredNodeStatusMap: framework.NodeToStatusMap{},
 			},
 			want:  nil,
 			want1: framework.NewStatus(framework.Unschedulable),
@@ -1858,7 +1867,8 @@ func TestPostFilter(t *testing.T) {
 			args: args{
 				hasStateData:             true,
 				hasAffinity:              true,
-				nodeReservationDiagnosis: map[string]nodeDiagnosisState{},
+				nodeReservationDiagnosis: map[string]*nodeDiagnosisState{},
+				filteredNodeStatusMap:    framework.NodeToStatusMap{},
 			},
 			want:  nil,
 			want1: framework.NewStatus(framework.Unschedulable, "0 Reservation(s) matched owner total"),
@@ -1867,7 +1877,7 @@ func TestPostFilter(t *testing.T) {
 			name: "show reservation owner matched, unschedulable unmatched",
 			args: args{
 				hasStateData: true,
-				nodeReservationDiagnosis: map[string]nodeDiagnosisState{
+				nodeReservationDiagnosis: map[string]*nodeDiagnosisState{
 					"test-node-0": {
 						ownerMatched:             3,
 						isUnschedulableUnmatched: 3,
@@ -1879,15 +1889,21 @@ func TestPostFilter(t *testing.T) {
 						affinityUnmatched:        0,
 					},
 				},
+				filteredNodeStatusMap: framework.NodeToStatusMap{
+					"test-node-0": {},
+					"test-node-1": {},
+				},
 			},
-			want:  nil,
-			want1: framework.NewStatus(framework.Unschedulable, "4 Reservation(s) is unschedulable", "4 Reservation(s) matched owner total"),
+			want: nil,
+			want1: framework.NewStatus(framework.Unschedulable,
+				"4 Reservation(s) is unschedulable",
+				"4 Reservation(s) matched owner total"),
 		},
 		{
 			name: "show reservation matched owner, unschedulable and affinity unmatched",
 			args: args{
 				hasStateData: true,
-				nodeReservationDiagnosis: map[string]nodeDiagnosisState{
+				nodeReservationDiagnosis: map[string]*nodeDiagnosisState{
 					"test-node-0": {
 						ownerMatched:             3,
 						isUnschedulableUnmatched: 0,
@@ -1899,9 +1915,72 @@ func TestPostFilter(t *testing.T) {
 						affinityUnmatched:        1,
 					},
 				},
+				filteredNodeStatusMap: framework.NodeToStatusMap{
+					"test-node-0": {},
+					"test-node-1": {},
+				},
 			},
-			want:  nil,
-			want1: framework.NewStatus(framework.Unschedulable, "4 Reservation(s) didn't match affinity rules", "1 Reservation(s) is unschedulable", "5 Reservation(s) matched owner total"),
+			want: nil,
+			want1: framework.NewStatus(framework.Unschedulable,
+				"4 Reservation(s) didn't match affinity rules",
+				"1 Reservation(s) is unschedulable",
+				"5 Reservation(s) matched owner total"),
+		},
+		{
+			name: "show reservation matched owner and filter failures of other plugins",
+			args: args{
+				hasStateData: true,
+				nodeReservationDiagnosis: map[string]*nodeDiagnosisState{
+					"test-node-0": {
+						ownerMatched:             3,
+						isUnschedulableUnmatched: 0,
+						affinityUnmatched:        3,
+					},
+					"test-node-1": {
+						ownerMatched:             3,
+						isUnschedulableUnmatched: 1,
+						affinityUnmatched:        0,
+					},
+				},
+				filteredNodeStatusMap: framework.NodeToStatusMap{
+					"test-node-0": {},
+					"test-node-1": testFilterStatus,
+				},
+			},
+			want: nil,
+			want1: framework.NewStatus(framework.Unschedulable,
+				"3 Reservation(s) didn't match affinity rules",
+				"1 Reservation(s) is unschedulable",
+				"2 Reservation(s) for node reason that node(s) didn't match the requested node name",
+				"6 Reservation(s) matched owner total"),
+		},
+		{
+			name: "show reservation matched owner and skip reservation-level reasons and filter other failures",
+			args: args{
+				hasStateData: true,
+				nodeReservationDiagnosis: map[string]*nodeDiagnosisState{
+					"test-node-0": {
+						ownerMatched:             3,
+						isUnschedulableUnmatched: 0,
+						affinityUnmatched:        1,
+					},
+					"test-node-1": {
+						ownerMatched:             3,
+						isUnschedulableUnmatched: 1,
+						affinityUnmatched:        0,
+					},
+				},
+				filteredNodeStatusMap: framework.NodeToStatusMap{
+					"test-node-0": testFilterReservationStatus,
+					"test-node-1": testFilterReservationStatus1,
+				},
+			},
+			want: nil,
+			want1: framework.NewStatus(framework.Unschedulable,
+				"1 Reservation(s) didn't match affinity rules",
+				"1 Reservation(s) is unschedulable",
+				"1 Reservation(s) for node reason that Insufficient memory",
+				"6 Reservation(s) matched owner total"),
 		},
 	}
 	for _, tt := range tests {
@@ -1914,7 +1993,7 @@ func TestPostFilter(t *testing.T) {
 					nodeReservationDiagnosis: tt.args.nodeReservationDiagnosis,
 				})
 			}
-			got, got1 := pl.PostFilter(context.TODO(), cycleState, nil, nil)
+			got, got1 := pl.PostFilter(context.TODO(), cycleState, nil, tt.args.filteredNodeStatusMap)
 			assert.Equal(t, tt.want, got)
 			assert.Equal(t, tt.want1, got1)
 		})
diff --git a/pkg/scheduler/plugins/reservation/transformer_test.go b/pkg/scheduler/plugins/reservation/transformer_test.go
index 0bee83c2..e78099f3 100644
--- a/pkg/scheduler/plugins/reservation/transformer_test.go
+++ b/pkg/scheduler/plugins/reservation/transformer_test.go
@@ -322,7 +322,7 @@ func TestRestoreReservation(t *testing.T) {
 				rAllocated: framework.NewResource(nil),
 			},
 		},
-		nodeReservationDiagnosis: map[string]nodeDiagnosisState{
+		nodeReservationDiagnosis: map[string]*nodeDiagnosisState{
 			node.Name: {
 				nodeName:                 node.Name,
 				ownerMatched:             1,
diff --git a/pkg/util/reservation/reservation_test.go b/pkg/util/reservation/reservation_test.go
index 3d8dfb20..4ff2c450 100644
--- a/pkg/util/reservation/reservation_test.go
+++ b/pkg/util/reservation/reservation_test.go
@@ -809,3 +809,11 @@ func TestGetReservationRestrictedResources(t *testing.T) {
 		})
 	}
 }
+
+func TestNewReservationReason(t *testing.T) {
+	t.Run("test", func(t *testing.T) {
+		reasonMsg := "node(s) didn't match the requested node name"
+		got := NewReservationReason(reasonMsg)
+		assert.True(t, IsReservationReason(got))
+	})
+}