Repository: koordinator-sh/koordinator

Issue:
[proposal] reservation controller 回收机制默认时间加入配置项
<!-- Please only use this template for submitting proposal -->

**What is your proposal**:

Inspired by [this](https://github.com/koordinator-sh/koordinator/pull/2399
) PR, we want to add a configurable GCDuration param in the reservation plugin arg. Currently, the default is to delete after 24 hours. We hope that the failure info can be retained for at least three days. This is an optional configuration for resource management traceability.

https://github.com/koordinator-sh/koordinator/blob/b652a3b1348d99f31ad1b81e89cd6df19c3cb3ed/pkg/scheduler/plugins/reservation/controller/garbage_collection.go#L33-L37

**Why is this needed**:

make more configurable for reservation controller

**Is there a suggested solution, if so, please add it**:


Discussion:
@googs1025: /assign@googs1025: cc @saintube 

Solution patch:
diff --git a/pkg/descheduler/framework/plugins/loadaware/utilization_util.go b/pkg/descheduler/framework/plugins/loadaware/utilization_util.go
index 6ffb087b..f7ad8028 100644
--- a/pkg/descheduler/framework/plugins/loadaware/utilization_util.go
+++ b/pkg/descheduler/framework/plugins/loadaware/utilization_util.go
@@ -440,6 +440,10 @@ func balancePods(ctx context.Context,
 				}
 				podNamespacedName := types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}
 				podMetric := srcNode.podMetrics[podNamespacedName]
+				if podMetric == nil {
+					klog.V(4).InfoS("Failed to find PodMetric", "pod", klog.KObj(pod), "node", klog.KObj(srcNode.node), "nodePool", nodePoolName)
+					return false
+				}
 				return podFitsAnyNodeWithThreshold(nodeIndexer, pod, targetNodes, nodeUsages, nodeThresholds, prod, podMetric)
 			}),
 		)
diff --git a/pkg/features/koordlet_features.go b/pkg/features/koordlet_features.go
index b82ef49f..de88eed0 100644
--- a/pkg/features/koordlet_features.go
+++ b/pkg/features/koordlet_features.go
@@ -122,6 +122,12 @@ const (
 	// Libpfm4 enables libpfm4 feature of koordlet.
 	Libpfm4 featuregate.Feature = "Libpfm4"
 
+	// owner: @Rouzip
+	// alpha: v0.1
+	//
+	// ResctrlCollector enables resctrl collector feature of koordlet.
+	ResctrlCollector featuregate.Feature = "ResctrlCollector"
+
 	// owner: @songtao98 @zwzhang0107
 	// alpha: v1.0
 	//
@@ -174,6 +180,7 @@ var (
 		CPUBurst:               {Default: true, PreRelease: featuregate.Beta},
 		SystemConfig:           {Default: false, PreRelease: featuregate.Alpha},
 		RdtResctrl:             {Default: true, PreRelease: featuregate.Beta},
+		ResctrlCollector:       {Default: false, PreRelease: featuregate.Alpha},
 		CgroupReconcile:        {Default: false, PreRelease: featuregate.Alpha},
 		NodeTopologyReport:     {Default: true, PreRelease: featuregate.Beta},
 		Accelerators:           {Default: false, PreRelease: featuregate.Alpha},
diff --git a/pkg/features/scheduler_features.go b/pkg/features/scheduler_features.go
index bc05e943..4bd67035 100644
--- a/pkg/features/scheduler_features.go
+++ b/pkg/features/scheduler_features.go
@@ -64,6 +64,12 @@ const (
 	// LazyReservationRestore is used to restore reserved resources lazily to improve the scheduling performance.
 	LazyReservationRestore featuregate.Feature = "LazyReservationRestore"
 
+	// owner: @saintube @ZiMengSheng
+	// alpha: v1.6
+	//
+	// OmitNodeLabelsForReservation is used to omit node labels while matching reservation affinity.
+	OmitNodeLabelsForReservation featuregate.Feature = "OmitNodeLabelsForReservation"
+
 	CSIStorageCapacity featuregate.Feature = "CSIStorageCapacity"
 
 	GenericEphemeralVolume featuregate.Feature = "GenericEphemeralVolume"
@@ -85,6 +91,7 @@ var defaultSchedulerFeatureGates = map[featuregate.Feature]featuregate.FeatureSp
 	DisableDefaultQuota:                       {Default: false, PreRelease: featuregate.Alpha},
 	SupportParentQuotaSubmitPod:               {Default: false, PreRelease: featuregate.Alpha},
 	LazyReservationRestore:                    {Default: false, PreRelease: featuregate.Alpha},
+	OmitNodeLabelsForReservation:              {Default: false, PreRelease: featuregate.Alpha},
 	CSIStorageCapacity:                        {Default: true, PreRelease: featuregate.GA}, // remove in 1.26
 	GenericEphemeralVolume:                    {Default: true, PreRelease: featuregate.GA},
 	PodDisruptionBudget:                       {Default: true, PreRelease: featuregate.GA},
diff --git a/pkg/koordlet/metriccache/metric_resources.go b/pkg/koordlet/metriccache/metric_resources.go
index 14b70b9e..623ab0e0 100644
--- a/pkg/koordlet/metriccache/metric_resources.go
+++ b/pkg/koordlet/metriccache/metric_resources.go
@@ -72,4 +72,8 @@ var (
 	HostAppCPUUsageMetric                 = defaultMetricFactory.New(HostAppCPUUsage).withPropertySchema(MetricPropertyHostAppName)
 	HostAppMemoryUsageMetric              = defaultMetricFactory.New(HostAppMemoryUsage).withPropertySchema(MetricPropertyHostAppName)
 	HostAppMemoryUsageWithPageCacheMetric = defaultMetricFactory.New(HostAppMemoryWithPageCacheUsage).withPropertySchema(MetricPropertyHostAppName)
+
+	// Resctrl
+	ResctrlLLCMetric = defaultMetricFactory.New(ResctrlLLC).withPropertySchema(MetricPropertyQos, MetricPropertyResctrlCacheId)
+	ResctrlMBMetric  = defaultMetricFactory.New(ResctrlMB).withPropertySchema(MetricPropertyQos, MetricPropertyResctrlCacheId, MetricPropertyResctrlMbType)
 )
diff --git a/pkg/koordlet/metriccache/metric_types.go b/pkg/koordlet/metriccache/metric_types.go
index a0b84c4d..15d53c58 100644
--- a/pkg/koordlet/metriccache/metric_types.go
+++ b/pkg/koordlet/metriccache/metric_types.go
@@ -18,6 +18,7 @@ package metriccache
 
 import (
 	"fmt"
+	"strconv"
 	"time"
 )
 
@@ -73,6 +74,10 @@ const (
 	// CPI
 	ContainerMetricCPI MetricKind = "container_cpi"
 
+	// Resctrl
+	ResctrlLLC MetricKind = "resctrl_resource_llc"
+	ResctrlMB  MetricKind = "resctrl_resource_mb"
+
 	// PSI
 	ContainerMetricPSI                 MetricKind = "container_psi"
 	ContainerMetricPSICPUFullSupported MetricKind = "container_psi_cpu_full_supported"
@@ -102,6 +107,12 @@ const (
 
 	MetricPropertyCPIResource MetricProperty = "cpi_resource"
 
+	MetricPropertyQos MetricProperty = "qos"
+
+	MetricPropertyResctrlType    MetricProperty = "resctrl_type"
+	MetricPropertyResctrlCacheId MetricProperty = "cache_id"
+	MetricPropertyResctrlMbType  MetricProperty = "resctrl_mb_type"
+
 	MetricPropertyPSIResource  MetricProperty = "psi_resource"
 	MetricPropertyPSIPrecision MetricProperty = "psi_precision"
 	MetricPropertyPSIDegree    MetricProperty = "psi_degree"
@@ -128,6 +139,9 @@ const (
 	PSIDegreeFull   MetricPropertyValue = "full"
 	PSIDegreeSome   MetricPropertyValue = "some"
 
+	ResctrlTypeLLC MetricPropertyValue = "llc"
+	ResctrlTypeMB  MetricPropertyValue = "mb"
+
 	BEResourceCPU                 MetricPropertyValue = "cpu"
 	BEResourceAllocationUsage     MetricPropertyValue = "usage"
 	BEResourceAllocationRealLimit MetricPropertyValue = "real-limit"
@@ -141,6 +155,8 @@ var MetricPropertiesFunc = struct {
 	GPU                 func(string, string) map[MetricProperty]string
 	PSICPUFullSupported func(string, string) map[MetricProperty]string
 	ContainerCPI        func(string, string, string) map[MetricProperty]string
+	ResctrlLLC          func(string, int) map[MetricProperty]string
+	ResctrlMB           func(string, int, string) map[MetricProperty]string
 	PodPSI              func(string, string, string, string) map[MetricProperty]string
 	ContainerPSI        func(string, string, string, string, string) map[MetricProperty]string
 	PodGPU              func(string, string, string) map[MetricProperty]string
@@ -160,6 +176,21 @@ var MetricPropertiesFunc = struct {
 	PSICPUFullSupported: func(podUID, containerID string) map[MetricProperty]string {
 		return map[MetricProperty]string{MetricPropertyPodUID: podUID, MetricPropertyContainerID: containerID}
 	},
+	ResctrlLLC: func(qos string, cacheid int) map[MetricProperty]string {
+		return map[MetricProperty]string{
+			MetricPropertyResctrlCacheId: strconv.Itoa(cacheid),
+			MetricPropertyQos:            qos,
+		}
+	},
+	ResctrlMB: func(qos string, cacheid int, mbType string) map[MetricProperty]string {
+		return map[MetricProperty]string{
+			MetricPropertyResctrlCacheId: strconv.Itoa(cacheid),
+			MetricPropertyResctrlType:    string(ResctrlTypeMB),
+			MetricPropertyResctrlMbType:  mbType,
+			MetricPropertyQos:            qos,
+		}
+
+	},
 	ContainerCPI: func(podUID, containerID, cpiResource string) map[MetricProperty]string {
 		return map[MetricProperty]string{MetricPropertyPodUID: podUID, MetricPropertyContainerID: containerID, MetricPropertyCPIResource: cpiResource}
 	},
diff --git a/pkg/koordlet/metrics/external_metrics.go b/pkg/koordlet/metrics/external_metrics.go
index c87d2a64..4eb9f55c 100644
--- a/pkg/koordlet/metrics/external_metrics.go
+++ b/pkg/koordlet/metrics/external_metrics.go
@@ -35,4 +35,5 @@ func init() {
 	ExternalMustRegister(ResourceSummaryCollectors...)
 	ExternalMustRegister(CPICollectors...)
 	ExternalMustRegister(PSICollectors...)
+	ExternalMustRegister(ResctrlCollectors...)
 }
diff --git a/pkg/koordlet/metrics/resctrl.go b/pkg/koordlet/metrics/resctrl.go
new file mode 100644
index 00000000..6c537972
--- /dev/null
+++ b/pkg/koordlet/metrics/resctrl.go
@@ -0,0 +1,74 @@
+/*
+Copyright 2022 The Koordinator Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package metrics
+
+import (
+	"strconv"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+const (
+	ResourceTypeLLC = "llc"
+	ResourceTypeMB  = "mb"
+
+	ResctrlResourceType = "resource_type"
+	ResctrlCacheId      = "cache_id"
+	ResctrlQos          = "qos"
+	ResctrlMbType       = "mb_type"
+)
+
+var (
+	ResctrlLLC = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Subsystem: KoordletSubsystem,
+		Name:      "resctrl_llc_occupancy",
+		Help:      "resctrl default qos(LSR, LS, BE) llc occupancy collected by koordlet",
+	}, []string{NodeKey, ResctrlCacheId, ResctrlQos})
+	ResctrlMB = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Subsystem: KoordletSubsystem,
+		Name:      "resctrl_memory_bandwidth",
+		Help:      "resctrl default qos(LSR, LS, BE) memory bandwidth collected by koordlet",
+	}, []string{NodeKey, ResctrlCacheId, ResctrlQos, ResctrlMbType})
+
+	ResctrlCollectors = []prometheus.Collector{
+		ResctrlLLC,
+		ResctrlMB,
+	}
+)
+
+func ResetResctrlLLCQos() {
+	ResctrlLLC.Reset()
+}
+
+func ResetResctrlMBQos() {
+	ResctrlMB.Reset()
+}
+
+func RecordResctrlLLC(cacheId int, qos string, value uint64) {
+	labels := genNodeLabels()
+	labels[ResctrlCacheId] = strconv.Itoa(cacheId)
+	labels[ResctrlQos] = qos
+	ResctrlLLC.With(labels).Set(float64(value))
+}
+
+func RecordResctrlMB(cacheId int, qos, mbType string, value uint64) {
+	labels := genNodeLabels()
+	labels[ResctrlCacheId] = strconv.Itoa(cacheId)
+	labels[ResctrlQos] = qos
+	labels[ResctrlMbType] = mbType
+	ResctrlMB.With(labels).Set(float64(value))
+}
diff --git a/pkg/koordlet/metricsadvisor/collectors/resctrl/resctrl_collector.go b/pkg/koordlet/metricsadvisor/collectors/resctrl/resctrl_collector.go
new file mode 100644
index 00000000..6d5ca964
--- /dev/null
+++ b/pkg/koordlet/metricsadvisor/collectors/resctrl/resctrl_collector.go
@@ -0,0 +1,151 @@
+/*
+Copyright 2022 The Koordinator Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package resctrl
+
+import (
+	"time"
+
+	"go.uber.org/atomic"
+	"k8s.io/apimachinery/pkg/util/wait"
+	"k8s.io/klog/v2"
+
+	"github.com/koordinator-sh/koordinator/pkg/features"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/metriccache"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/metrics"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/metricsadvisor/framework"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/qosmanager/plugins/resctrl"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/resourceexecutor"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/statesinformer"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/util/system"
+)
+
+const (
+	CollectorName = "resctrlCollector"
+)
+
+type resctrlCollector struct {
+	collectInterval      time.Duration
+	started              *atomic.Bool
+	metricCache          metriccache.MetricCache
+	statesInformer       statesinformer.StatesInformer
+	resctrlReader        resourceexecutor.ResctrlReader
+	resctrlCollectorGate bool
+}
+
+func New(opt *framework.Options) framework.Collector {
+	return &resctrlCollector{
+		collectInterval:      opt.Config.ResctrlCollectorInterval,
+		statesInformer:       opt.StatesInformer,
+		metricCache:          opt.MetricCache,
+		resctrlReader:        resourceexecutor.NewResctrlReader(),
+		resctrlCollectorGate: opt.Config.EnableResctrlCollector,
+		started:              atomic.NewBool(false),
+	}
+}
+
+// 1. config enable resctrl collector
+// 2. cmdline, os, cpuid enable resctrl collector
+// 3. check CPU vendor(Intel&AMD)
+// 4. check resctrl collector feature gate
+func (r *resctrlCollector) Enabled() bool {
+	isResctrlEnabled, _ := system.IsSupportResctrl()
+	isResctrlCollectorEnabled, _ := system.IsSupportResctrlCollector()
+	return r.resctrlCollectorGate &&
+		isResctrlEnabled && isResctrlCollectorEnabled &&
+		system.IsVendorSupportResctrl() &&
+		features.DefaultKoordletFeatureGate.Enabled(features.ResctrlCollector)
+}
+
+func (r *resctrlCollector) Setup(c *framework.Context) {}
+
+func (r *resctrlCollector) Started() bool {
+	return r.started.Load()
+}
+
+func (r *resctrlCollector) Run(stopCh <-chan struct{}) {
+	go wait.Until(r.collectQoSResctrlStat, r.collectInterval, stopCh)
+}
+
+func (r *resctrlCollector) collectQoSResctrlStat() {
+	err := system.CheckResctrlSchemataValid()
+	if err != nil {
+		// use resctrl/schemata to check mount state TODO: use another method to check
+		klog.V(4).Infof("cannot find resctrl file system schemata, error: %v", err)
+		return
+	}
+	klog.V(6).Info("start collect QoS resctrl Stat")
+	resctrlMetrics := make([]metriccache.MetricSample, 0)
+	collectTime := time.Now()
+	for _, qos := range []string{
+		resctrl.LSRResctrlGroup,
+		resctrl.LSResctrlGroup,
+		resctrl.BEResctrlGroup} {
+		l3Map, err := r.resctrlReader.ReadResctrlL3Stat(qos)
+		if err != nil {
+			klog.V(4).Infof("collect QoS %s resctrl llc data error: %v", qos, err)
+			continue
+		}
+		for cacheId, value := range l3Map {
+			metrics.RecordResctrlLLC(int(cacheId), qos, value)
+			llcSample, err := metriccache.ResctrlLLCMetric.GenerateSample(metriccache.MetricPropertiesFunc.ResctrlLLC(qos, int(cacheId)), collectTime, float64(value))
+			if err != nil {
+				klog.Warningf("generate QoS %s resctrl llc sample error: %v", qos, err)
+			}
+			resctrlMetrics = append(resctrlMetrics, llcSample)
+		}
+		mbMap, err := r.resctrlReader.ReadResctrlMBStat(qos)
+		if err != nil {
+			klog.V(4).Infof("collect QoS %s resctrl mb data error: %v", qos, err)
+			continue
+		}
+		for cacheId, value := range mbMap {
+			for mbType, mbValue := range value {
+				metrics.RecordResctrlMB(int(cacheId), qos, mbType, mbValue)
+				mbSample, err := metriccache.ResctrlMBMetric.GenerateSample(metriccache.MetricPropertiesFunc.ResctrlMB(qos, int(cacheId), mbType), collectTime, float64(mbValue))
+				if err != nil {
+					klog.V(4).Infof("generate QoS %s resctrl mb sample error: %v", qos, err)
+				}
+				resctrlMetrics = append(resctrlMetrics, mbSample)
+			}
+		}
+	}
+
+	// save QoS resctrl data to tsdb
+	r.saveMetric(resctrlMetrics)
+
+	r.started.Store(true)
+	klog.V(6).Infof("collect resctrl data at %s", time.Now())
+}
+
+func (r *resctrlCollector) saveMetric(samples []metriccache.MetricSample) error {
+	if len(samples) == 0 {
+		return nil
+	}
+
+	appender := r.metricCache.Appender()
+	if err := appender.Append(samples); err != nil {
+		klog.ErrorS(err, "Append container metrics error")
+		return err
+	}
+
+	if err := appender.Commit(); err != nil {
+		klog.ErrorS(err, "Commit container metrics failed")
+		return err
+	}
+
+	return nil
+}
diff --git a/pkg/koordlet/metricsadvisor/framework/config.go b/pkg/koordlet/metricsadvisor/framework/config.go
index 0e505bd8..0bf8e97c 100644
--- a/pkg/koordlet/metricsadvisor/framework/config.go
+++ b/pkg/koordlet/metricsadvisor/framework/config.go
@@ -35,7 +35,9 @@ type Config struct {
 	PSICollectorInterval             time.Duration
 	CPICollectorTimeWindow           time.Duration
 	ColdPageCollectorInterval        time.Duration
+	ResctrlCollectorInterval         time.Duration
 	EnablePageCacheCollector         bool
+	EnableResctrlCollector           bool
 }
 
 func NewDefaultConfig() *Config {
@@ -48,7 +50,9 @@ func NewDefaultConfig() *Config {
 		PSICollectorInterval:             10 * time.Second,
 		CPICollectorTimeWindow:           10 * time.Second,
 		ColdPageCollectorInterval:        5 * time.Second,
+		ResctrlCollectorInterval:         10 * time.Second,
 		EnablePageCacheCollector:         false,
+		EnableResctrlCollector:           false,
 	}
 }
 
@@ -62,4 +66,6 @@ func (c *Config) InitFlags(fs *flag.FlagSet) {
 	fs.DurationVar(&c.CPICollectorTimeWindow, "collect-cpi-timewindow", c.CPICollectorTimeWindow, "Collect cpi time window. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
 	fs.DurationVar(&c.ColdPageCollectorInterval, "coldpage-collector-interval", c.ColdPageCollectorInterval, "Collect cold page interval. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
 	fs.BoolVar(&c.EnablePageCacheCollector, "enable-pagecache-collector", c.EnablePageCacheCollector, "Enable cache collector of node, pods and containers")
+	fs.BoolVar(&c.EnableResctrlCollector, "enable-resctrl-collector", c.EnableResctrlCollector, "Enable cache collector of node, pods and containers")
+	fs.DurationVar(&c.ResctrlCollectorInterval, "resctrl-collector-interval", c.ResctrlCollectorInterval, "Collect cpi time window. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
 }
diff --git a/pkg/koordlet/metricsadvisor/plugins_profile.go b/pkg/koordlet/metricsadvisor/plugins_profile.go
index 5d096bd7..9a876bf1 100644
--- a/pkg/koordlet/metricsadvisor/plugins_profile.go
+++ b/pkg/koordlet/metricsadvisor/plugins_profile.go
@@ -27,6 +27,7 @@ import (
 	"github.com/koordinator-sh/koordinator/pkg/koordlet/metricsadvisor/collectors/performance"
 	"github.com/koordinator-sh/koordinator/pkg/koordlet/metricsadvisor/collectors/podresource"
 	"github.com/koordinator-sh/koordinator/pkg/koordlet/metricsadvisor/collectors/podthrottled"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/metricsadvisor/collectors/resctrl"
 	"github.com/koordinator-sh/koordinator/pkg/koordlet/metricsadvisor/collectors/sysresource"
 	"github.com/koordinator-sh/koordinator/pkg/koordlet/metricsadvisor/devices/gpu"
 	"github.com/koordinator-sh/koordinator/pkg/koordlet/metricsadvisor/devices/rdma"
@@ -53,6 +54,7 @@ var (
 		coldmemoryresource.CollectorName: coldmemoryresource.New,
 		pagecache.CollectorName:          pagecache.New,
 		hostapplication.CollectorName:    hostapplication.New,
+		resctrl.CollectorName:            resctrl.New,
 	}
 
 	podFilters = map[string]framework.PodFilter{
diff --git a/pkg/koordlet/resourceexecutor/resctrl.go b/pkg/koordlet/resourceexecutor/resctrl.go
new file mode 100644
index 00000000..03112e75
--- /dev/null
+++ b/pkg/koordlet/resourceexecutor/resctrl.go
@@ -0,0 +1,171 @@
+/*
+Copyright 2022 The Koordinator Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package resourceexecutor
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"k8s.io/klog/v2"
+
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/util/system"
+)
+
+const ErrResctrlDir = "resctrl path or file not exist"
+const CacheIdIndex = 2
+
+// NewResctrlReader: lazy resctrl reader, just check vendor to generate specific reader
+func NewResctrlReader() ResctrlReader {
+	// Support two main platforms; other platforms need to add their implementation of the resctrl interface.
+	if vendorId, err := system.GetVendorIDByCPUInfo(system.GetCPUInfoPath()); err != nil {
+		klog.V(0).ErrorS(err, "get cpu vendor error, stop start resctrl collector")
+		return &fakeReader{}
+	} else {
+		switch vendorId {
+		case system.INTEL_VENDOR_ID:
+			return NewResctrlRDTReader()
+		case system.AMD_VENDOR_ID:
+			return NewResctrlQoSReader()
+		default:
+			klog.V(0).ErrorS(err, "unsupported cpu vendor")
+		}
+	}
+	return &fakeReader{}
+}
+
+type CacheId int
+
+// parent for resctrl is like: `BE`, `LS`
+type ResctrlReader interface {
+	ReadResctrlL3Stat(parent string) (map[CacheId]uint64, error)
+	ReadResctrlMBStat(parent string) (map[CacheId]system.MBStatData, error)
+}
+
+type ResctrlBaseReader struct {
+}
+
+type ResctrlRDTReader struct {
+	ResctrlBaseReader
+}
+type ResctrlAMDReader struct {
+	ResctrlBaseReader
+}
+
+type fakeReader struct {
+	ResctrlBaseReader
+}
+
+func (rr *fakeReader) ReadResctrlL3Stat(parent string) (map[CacheId]uint64, error) {
+	return nil, errors.New("unsupported platform")
+}
+
+func (rr *fakeReader) ReadResctrlMBStat(parent string) (map[CacheId]system.MBStatData, error) {
+	return nil, errors.New("unsupported platform")
+}
+
+func NewResctrlRDTReader() ResctrlReader {
+	return &ResctrlRDTReader{}
+}
+
+func NewResctrlQoSReader() ResctrlReader {
+	return &ResctrlAMDReader{}
+}
+
+// ReadResctrlL3Stat: Reads the resctrl L3 cache statistics based on NUMA domain.
+// For more information about x86 resctrl, refer to: https://docs.kernel.org/arch/x86/resctrl.html
+func (rr *ResctrlBaseReader) ReadResctrlL3Stat(parent string) (map[CacheId]uint64, error) {
+	l3Stat := make(map[CacheId]uint64)
+	monDataPath := system.GetResctrlMonDataPath(parent)
+	fd, err := os.Open(monDataPath)
+	if err != nil {
+		return nil, errors.New(ErrResctrlDir)
+	}
+	defer fd.Close()
+	// read all l3-memory domains
+	domains, err := fd.ReadDir(-1)
+	if err != nil {
+		return nil, fmt.Errorf("%s, cannot find L3 domains, err: %w", ErrResctrlDir, err)
+	}
+	for _, domain := range domains {
+		// Convert the cache ID from the domain name string to an integer.
+		cacheId, err := strconv.Atoi(strings.Split(domain.Name(), "_")[CacheIdIndex])
+		if err != nil {
+			return nil, fmt.Errorf("%s, cannot get cacheid, err: %w", ErrResctrlDir, err)
+		}
+		// Construct the path to the resctrl L3 cache occupancy file.
+		path := system.ResctrlLLCOccupancy.Path(filepath.Join(parent, system.ResctrlMonData, domain.Name()))
+		l3Byte, err := os.ReadFile(path)
+		if err != nil {
+			return nil, fmt.Errorf("%s, cannot read from resctrl file system, err: %w",
+				ErrResctrlDir, err)
+		}
+		// Parse the L3 cache usage data from the file content.
+		l3Usage, err := strconv.ParseUint(string(l3Byte), 10, 64)
+		if err != nil {
+			return nil, fmt.Errorf("cannot parse L3 cache usage, err: %w", err)
+		}
+		l3Stat[CacheId(cacheId)] = l3Usage
+	}
+	return l3Stat, nil
+}
+
+// ReadResctrlMBStat: Reads the resctrl memory bandwidth statistics based on NUMA domain.
+// For more information about x86 resctrl, refer to: https://docs.kernel.org/arch/x86/resctrl.html
+func (rr *ResctrlBaseReader) ReadResctrlMBStat(parent string) (map[CacheId]system.MBStatData, error) {
+	mbStat := make(map[CacheId]system.MBStatData)
+	monDataPath := system.GetResctrlMonDataPath(parent)
+	fd, err := os.Open(monDataPath)
+	if err != nil {
+		return nil, errors.New(ErrResctrlDir)
+	}
+	// read all l3-memory domains
+	domains, err := fd.ReadDir(-1)
+	if err != nil {
+		return nil, fmt.Errorf("%s, cannot find L3 domains, err: %w", ErrResctrlDir, err)
+	}
+	for _, domain := range domains {
+		// Parse the L3 cache usage data from the file content.
+		cacheId, err := strconv.Atoi(strings.Split(domain.Name(), "_")[CacheIdIndex])
+		if err != nil {
+			return nil, fmt.Errorf("%s, cannot get cacheid, err: %w", ErrResctrlDir, err)
+		}
+		mbStat[CacheId(cacheId)] = make(system.MBStatData)
+		// Read the memory bandwidth statistics for the local and total memory bandwidth.
+		// The local memory bandwidth is the memory bandwidth consumed by the domain itself.
+		// The total memory bandwidth is the memory bandwidth consumed by the domain and accessed by other domains.
+		for _, mbResource := range []system.Resource{
+			system.ResctrlMBLocal, system.ResctrlMBTotal,
+		} {
+			contentName := mbResource.Path(filepath.Join(parent, system.ResctrlMonData, domain.Name()))
+			contentByte, err := os.ReadFile(contentName)
+			if err != nil {
+				return nil, fmt.Errorf("%s, cannot read from resctrl file system, err: %w",
+					ErrResctrlDir, err)
+			}
+			mbUsage, err := strconv.ParseUint(string(contentByte), 10, 64)
+			if err != nil {
+				return nil, fmt.Errorf("cannot parse result, err: %w", err)
+			}
+			mbStat[CacheId(cacheId)][string(mbResource.ResourceType())] = mbUsage
+		}
+	}
+	return mbStat, nil
+}
diff --git a/pkg/koordlet/util/system/config.go b/pkg/koordlet/util/system/config.go
index 68c45f0d..8f365b77 100644
--- a/pkg/koordlet/util/system/config.go
+++ b/pkg/koordlet/util/system/config.go
@@ -75,6 +75,11 @@ func InitSupportConfigs() {
 	} else {
 		klog.V(4).Infof("resctrl supported: %v", isResctrlSupported)
 	}
+	if isResctrlCollectorSupported, err := IsSupportResctrlCollector(); err != nil {
+		klog.Warningf("failed to check resctrl collector support status, use %v, err: %v", isResctrlCollectorSupported, err)
+	} else {
+		klog.V(4).Infof("resctrl collector supported: %v", isResctrlCollectorSupported)
+	}
 }
 
 func NewHostModeConfig() *Config {
diff --git a/pkg/koordlet/util/system/resctrl.go b/pkg/koordlet/util/system/resctrl.go
index d4ed3368..aa55a178 100644
--- a/pkg/koordlet/util/system/resctrl.go
+++ b/pkg/koordlet/util/system/resctrl.go
@@ -29,6 +29,7 @@ import (
 	"strings"
 	"sync"
 
+	"go.uber.org/multierr"
 	"k8s.io/klog/v2"
 
 	"github.com/koordinator-sh/koordinator/pkg/util"
@@ -50,16 +51,24 @@ const (
 	// MbSchemataPrefix is the prefix of mba schemata
 	MbSchemataPrefix = "MB"
 
+	ResctrlMonData          = "mon_data"
+	ResctrlLLCOccupancyName = "llc_occupancy"
+	ResctrlMBMLocalName     = "mbm_local_bytes"
+	ResctrlMBMTotalName     = "mbm_total_bytes"
+
 	// other cpu vendor like "GenuineIntel"
 	AMD_VENDOR_ID   = "AuthenticAMD"
 	INTEL_VENDOR_ID = "GenuineIntel"
 )
 
 var (
-	initLock          sync.Mutex
-	isInit            bool
-	isSupportResctrl  bool
-	CacheIdsCacheFunc func() ([]int, error)
+	initLock                  sync.Mutex
+	isInit                    bool
+	isSupportResctrl          bool
+	isSupportResctrlCollector bool
+	collectorOnceFunc         sync.Once
+	CacheIdsCacheFunc         func() ([]int, error)
+	ARM_VENDOR_ID_MAP         = map[string]struct{}{} // support MPAM ARM vendor ids
 )
 
 func init() {
@@ -91,6 +100,18 @@ func isKernelSupportResctrl() (bool, error) {
 	return isCatFlagSet && isMbaFlagSet, nil
 }
 
+func IsVendorSupportResctrl() bool {
+	vendorID, err := GetVendorIDByCPUInfo(GetCPUInfoPath())
+	if err != nil {
+		klog.V(4).Infof("GetVendorIDByCPUInfo error: %v", err)
+		return false
+	}
+	if _, ok := ARM_VENDOR_ID_MAP[vendorID]; ok {
+		return true
+	}
+	return vendorID == AMD_VENDOR_ID || vendorID == INTEL_VENDOR_ID
+}
+
 func IsSupportResctrl() (bool, error) {
 	initLock.Lock()
 	defer initLock.Unlock()
@@ -104,7 +125,7 @@ func IsSupportResctrl() (bool, error) {
 			return false, err
 		}
 		// Kernel cmdline not set resctrl features does not ensure feature must be disabled.
-		klog.Infof("IsSupportResctrl result, cpuSupport: %v, kernelSupport: %v",
+		klog.V(4).Infof("isResctrlAvailableByKernelCmd result, cpuSupport: %v, kernelSupport: %v",
 			cpuSupport, kernelCmdSupport)
 		isSupportResctrl = cpuSupport
 		isInit = true
@@ -112,11 +133,26 @@ func IsSupportResctrl() (bool, error) {
 	return isSupportResctrl, nil
 }
 
+func IsSupportResctrlCollector() (bool, error) {
+	var err error
+	collectorOnceFunc.Do(func() {
+		path := GetCPUInfoPath()
+		mbm, err1 := isResctrlMBMAvailableByCpuInfo(path)
+		cqm, err2 := isResctrlCQMAvailableByCpuInfo(path)
+		isSupportResctrlCollector = mbm && cqm
+		err = multierr.Append(err1, err2)
+	})
+	return isSupportResctrlCollector, err
+}
+
 var (
-	ResctrlRoot      = NewCommonResctrlResource("", "")
-	ResctrlSchemata  = NewCommonResctrlResource(ResctrlSchemataName, "")
-	ResctrlTasks     = NewCommonResctrlResource(ResctrlTasksName, "")
-	ResctrlL3CbmMask = NewCommonResctrlResource(ResctrlCbmMaskName, filepath.Join(RdtInfoDir, L3CatDir))
+	ResctrlRoot         = NewCommonResctrlResource("", "")
+	ResctrlSchemata     = NewCommonResctrlResource(ResctrlSchemataName, "")
+	ResctrlTasks        = NewCommonResctrlResource(ResctrlTasksName, "")
+	ResctrlL3CbmMask    = NewCommonResctrlResource(ResctrlCbmMaskName, filepath.Join(RdtInfoDir, L3CatDir))
+	ResctrlLLCOccupancy = NewCommonResctrlResource(ResctrlLLCOccupancyName, "")
+	ResctrlMBLocal      = NewCommonResctrlResource(ResctrlMBMLocalName, "")
+	ResctrlMBTotal      = NewCommonResctrlResource(ResctrlMBMTotalName, "")
 )
 
 var _ Resource = &ResctrlResource{}
@@ -445,6 +481,12 @@ func GetResctrlTasksFilePath(groupPath string) string {
 	return ResctrlTasks.Path(groupPath)
 }
 
+// @parentDir BE
+// @return /sys/fs/resctrl/BE/mon_data
+func GetResctrlMonDataPath(parentDir string) string {
+	return filepath.Join(Conf.SysFSRootDir, ResctrlDir, parentDir, ResctrlMonData)
+}
+
 func ReadResctrlSchemataRaw(schemataFile string, l3Num int) (*ResctrlSchemataRaw, error) {
 	content, err := os.ReadFile(schemataFile)
 	if err != nil {
@@ -666,36 +708,72 @@ func isResctrlAvailableByCpuInfo(path string) (bool, bool, error) {
 	isCatFlagSet := false
 	isMbaFlagSet := false
 
-	f, err := os.Open(path)
+	flagMap, err := getCPUFlags(path)
 	if err != nil {
 		return false, false, err
 	}
-	defer f.Close()
 
+	if _, exist := flagMap["cat_l3"]; exist {
+		isCatFlagSet = true
+	}
+	if _, exist := flagMap["mba"]; exist {
+		isMbaFlagSet = true
+	}
+
+	return isCatFlagSet, isMbaFlagSet, nil
+}
+
+func isResctrlMBMAvailableByCpuInfo(path string) (bool, error) {
+	flagMap, err := getCPUFlags(path)
+	if err != nil {
+		return false, err
+	}
+
+	isMBMTotalFlagSet, isMBMLocalFlagSet := false, false
+
+	if _, exist := flagMap["cqm_mbm_total"]; exist {
+		isMBMTotalFlagSet = true
+	}
+	if _, exist := flagMap["cqm_mbm_local"]; exist {
+		isMBMLocalFlagSet = true
+	}
+
+	return isMBMLocalFlagSet && isMBMTotalFlagSet, nil
+}
+
+func isResctrlCQMAvailableByCpuInfo(path string) (bool, error) {
+	flagMap, err := getCPUFlags(path)
+	if err != nil {
+		return false, err
+	}
+
+	_, existFlag1 := flagMap["cqm_llc"]
+	_, existFlag2 := flagMap["cqm_occup_llc"]
+	return existFlag1 && existFlag2, nil
+}
+
+func getCPUFlags(path string) (map[string]struct{}, error) {
+	f, err := os.Open(path)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	flagsMap := make(map[string]struct{})
 	s := bufio.NewScanner(f)
 	for s.Scan() {
 		if err := s.Err(); err != nil {
-			return false, false, err
+			return nil, err
 		}
 
 		line := s.Text()
-
-		// Search "cat_l3" and "mba" flags in first "flags" line
 		if strings.Contains(line, "flags") {
 			flags := strings.Split(line, " ")
-			// "cat_l3" flag for CAT and "mba" flag for MBA
 			for _, flag := range flags {
-				switch flag {
-				case "cat_l3":
-					isCatFlagSet = true
-				case "mba":
-					isMbaFlagSet = true
-				}
+				flagsMap[flag] = struct{}{}
 			}
-			return isCatFlagSet, isMbaFlagSet, nil
 		}
 	}
-	return isCatFlagSet, isMbaFlagSet, nil
+	return flagsMap, nil
 }
 
 // file content example:
@@ -729,3 +807,5 @@ func isResctrlAvailableByKernelCmd(path string) (bool, bool, error) {
 	}
 	return isCatFlagSet, isMbaFlagSet, nil
 }
+
+type MBStatData map[string]uint64
diff --git a/pkg/scheduler/apis/config/types.go b/pkg/scheduler/apis/config/types.go
index 5b9f8777..a110818c 100644
--- a/pkg/scheduler/apis/config/types.go
+++ b/pkg/scheduler/apis/config/types.go
@@ -178,6 +178,10 @@ type ReservationArgs struct {
 	// Workers number of reservation controller.
 	// Defaults to 1 if unspecified.
 	ControllerWorkers int32
+	// GCDurationSeconds is the duration in seconds after which expired or succeeded reservations
+	// will be garbage collected. Defaults to 24 hours (86400 seconds) if unspecified.
+	// This value should be provided in seconds.
+	GCDurationSeconds int64
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
diff --git a/pkg/scheduler/apis/config/v1/defaults.go b/pkg/scheduler/apis/config/v1/defaults.go
index 6d19b9e3..1bf20f93 100644
--- a/pkg/scheduler/apis/config/v1/defaults.go
+++ b/pkg/scheduler/apis/config/v1/defaults.go
@@ -53,6 +53,7 @@ var (
 	defaultMinCandidateNodesPercentage  = pointer.Int32(10)
 	defaultMinCandidateNodesAbsolute    = pointer.Int32(100)
 	defaultReservationControllerWorkers = pointer.Int32(1)
+	defaultGCDurationSeconds            = pointer.Int64(86400)
 
 	defaultDelayEvictTime       = 120 * time.Second
 	defaultRevokePodInterval    = 1 * time.Second
@@ -155,6 +156,9 @@ func SetDefaults_ReservationArgs(obj *ReservationArgs) {
 	if obj.ControllerWorkers == nil {
 		obj.ControllerWorkers = defaultReservationControllerWorkers
 	}
+	if obj.GCDurationSeconds == 0 {
+		obj.GCDurationSeconds = *defaultGCDurationSeconds
+	}
 }
 
 func SetDefaults_ElasticQuotaArgs(obj *ElasticQuotaArgs) {
diff --git a/pkg/scheduler/apis/config/v1/types.go b/pkg/scheduler/apis/config/v1/types.go
index fdd76a51..5b7d6452 100644
--- a/pkg/scheduler/apis/config/v1/types.go
+++ b/pkg/scheduler/apis/config/v1/types.go
@@ -173,6 +173,10 @@ type ReservationArgs struct {
 	// Workers number of reservation controller.
 	// Defaults to 1 if unspecified.
 	ControllerWorkers *int32 `json:"controllerWorkers,omitempty"`
+	// GCDurationSeconds is the duration in seconds after which expired or succeeded reservations
+	// will be garbage collected. Defaults to 24 hours (86400 seconds) if unspecified.
+	// This value should be provided in seconds.
+	GCDurationSeconds int64 `json:"gcDurationSeconds,omitempty"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
diff --git a/pkg/scheduler/apis/config/v1/zz_generated.conversion.go b/pkg/scheduler/apis/config/v1/zz_generated.conversion.go
index f18b9eeb..45b9ad02 100644
--- a/pkg/scheduler/apis/config/v1/zz_generated.conversion.go
+++ b/pkg/scheduler/apis/config/v1/zz_generated.conversion.go
@@ -423,6 +423,7 @@ func autoConvert_v1_ReservationArgs_To_config_ReservationArgs(in *ReservationArg
 	if err := metav1.Convert_Pointer_int32_To_int32(&in.ControllerWorkers, &out.ControllerWorkers, s); err != nil {
 		return err
 	}
+	out.GCDurationSeconds = in.GCDurationSeconds
 	return nil
 }
 
@@ -444,6 +445,7 @@ func autoConvert_config_ReservationArgs_To_v1_ReservationArgs(in *config.Reserva
 	if err := metav1.Convert_int32_To_Pointer_int32(&in.ControllerWorkers, &out.ControllerWorkers, s); err != nil {
 		return err
 	}
+	out.GCDurationSeconds = in.GCDurationSeconds
 	return nil
 }
 
diff --git a/pkg/scheduler/apis/config/v1beta3/defaults.go b/pkg/scheduler/apis/config/v1beta3/defaults.go
index b3c9a847..7d607339 100644
--- a/pkg/scheduler/apis/config/v1beta3/defaults.go
+++ b/pkg/scheduler/apis/config/v1beta3/defaults.go
@@ -53,6 +53,7 @@ var (
 	defaultMinCandidateNodesPercentage  = pointer.Int32(10)
 	defaultMinCandidateNodesAbsolute    = pointer.Int32(100)
 	defaultReservationControllerWorkers = pointer.Int32(1)
+	defaultGCDurationSeconds            = pointer.Int64(86400)
 
 	defaultDelayEvictTime       = 120 * time.Second
 	defaultRevokePodInterval    = 1 * time.Second
@@ -155,6 +156,9 @@ func SetDefaults_ReservationArgs(obj *ReservationArgs) {
 	if obj.ControllerWorkers == nil {
 		obj.ControllerWorkers = defaultReservationControllerWorkers
 	}
+	if obj.GCDurationSeconds == 0 {
+		obj.GCDurationSeconds = *defaultGCDurationSeconds
+	}
 }
 
 func SetDefaults_ElasticQuotaArgs(obj *ElasticQuotaArgs) {
diff --git a/pkg/scheduler/apis/config/v1beta3/types.go b/pkg/scheduler/apis/config/v1beta3/types.go
index a15c24f1..c978742f 100644
--- a/pkg/scheduler/apis/config/v1beta3/types.go
+++ b/pkg/scheduler/apis/config/v1beta3/types.go
@@ -173,6 +173,10 @@ type ReservationArgs struct {
 	// Workers number of reservation controller.
 	// Defaults to 1 if unspecified.
 	ControllerWorkers *int32 `json:"controllerWorkers,omitempty"`
+	// GCDurationSeconds is the duration in seconds after which expired or succeeded reservations
+	// will be garbage collected. Defaults to 24 hours (86400 seconds) if unspecified.
+	// This value should be provided in seconds.
+	GCDurationSeconds int64 `json:"gcDurationSeconds,omitempty"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
diff --git a/pkg/scheduler/apis/config/v1beta3/zz_generated.conversion.go b/pkg/scheduler/apis/config/v1beta3/zz_generated.conversion.go
index 27fa73bd..872a0b31 100644
--- a/pkg/scheduler/apis/config/v1beta3/zz_generated.conversion.go
+++ b/pkg/scheduler/apis/config/v1beta3/zz_generated.conversion.go
@@ -418,6 +418,7 @@ func autoConvert_v1beta3_ReservationArgs_To_config_ReservationArgs(in *Reservati
 	if err := v1.Convert_Pointer_int32_To_int32(&in.ControllerWorkers, &out.ControllerWorkers, s); err != nil {
 		return err
 	}
+	out.GCDurationSeconds = in.GCDurationSeconds
 	return nil
 }
 
@@ -439,6 +440,7 @@ func autoConvert_config_ReservationArgs_To_v1beta3_ReservationArgs(in *config.Re
 	if err := v1.Convert_int32_To_Pointer_int32(&in.ControllerWorkers, &out.ControllerWorkers, s); err != nil {
 		return err
 	}
+	out.GCDurationSeconds = in.GCDurationSeconds
 	return nil
 }
 
diff --git a/pkg/scheduler/apis/config/validation/validation_pluginargs.go b/pkg/scheduler/apis/config/validation/validation_pluginargs.go
index 30bc3284..0725873e 100644
--- a/pkg/scheduler/apis/config/validation/validation_pluginargs.go
+++ b/pkg/scheduler/apis/config/validation/validation_pluginargs.go
@@ -153,6 +153,39 @@ func ValidateDeviceShareArgs(path *field.Path, args *config.DeviceShareArgs) err
 	return allErrs.ToAggregate()
 }
 
+func ValidateReservationArgs(path *field.Path, args *config.ReservationArgs) error {
+	var allErrs field.ErrorList
+
+	if args.MinCandidateNodesPercentage < 0 || args.MinCandidateNodesPercentage > 100 {
+		allErrs = append(allErrs, field.Invalid(
+			path.Child("MinCandidateNodesPercentage"),
+			args.MinCandidateNodesPercentage,
+			"must be in the range [0, 100]",
+		))
+	}
+
+	if args.MinCandidateNodesAbsolute < 0 {
+		allErrs = append(allErrs, field.Invalid(
+			path.Child("MinCandidateNodesAbsolute"),
+			args.MinCandidateNodesAbsolute,
+			"must be non-negative",
+		))
+	}
+
+	if args.GCDurationSeconds < 0 {
+		allErrs = append(allErrs, field.Invalid(
+			path.Child("GcDuration"),
+			args.GCDurationSeconds,
+			"must be non-negative",
+		))
+	}
+
+	if len(allErrs) == 0 {
+		return nil
+	}
+	return allErrs.ToAggregate()
+}
+
 func ValidateNodeNUMAResourceArgs(path *field.Path, args *config.NodeNUMAResourceArgs) error {
 	var allErrs field.ErrorList
 	if args.DefaultCPUBindPolicy != "" &&
diff --git a/pkg/scheduler/frameworkext/reservation_info.go b/pkg/scheduler/frameworkext/reservation_info.go
index 06b4d66c..048190c3 100644
--- a/pkg/scheduler/frameworkext/reservation_info.go
+++ b/pkg/scheduler/frameworkext/reservation_info.go
@@ -22,6 +22,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	utilerrors "k8s.io/apimachinery/pkg/util/errors"
 	quotav1 "k8s.io/apiserver/pkg/quota/v1"
+	k8sfeature "k8s.io/apiserver/pkg/util/feature"
 	corev1helpers "k8s.io/component-helpers/scheduling/corev1"
 	"k8s.io/klog/v2"
 	k8spodutil "k8s.io/kubernetes/pkg/api/v1/pod"
@@ -30,6 +31,7 @@ import (
 
 	apiext "github.com/koordinator-sh/koordinator/apis/extension"
 	schedulingv1alpha1 "github.com/koordinator-sh/koordinator/apis/scheduling/v1alpha1"
+	"github.com/koordinator-sh/koordinator/pkg/features"
 	"github.com/koordinator-sh/koordinator/pkg/util"
 	reservationutil "github.com/koordinator-sh/koordinator/pkg/util/reservation"
 )
@@ -47,6 +49,7 @@ type ReservationInfo struct {
 	AssignedPods     map[types.UID]*PodRequirement
 	OwnerMatchers    []reservationutil.ReservationOwnerMatcher
 	ParseError       error
+	Labels           map[string]string
 }
 
 type PodRequirement struct {
@@ -303,22 +306,29 @@ func (ri *ReservationInfo) GetTaints() []corev1.Taint {
 
 // MatchReservationAffinity returns the statuses of whether the reservation affinity matches, whether the reservation
 // taints are tolerated, and whether the reservation name matches.
-func (ri *ReservationInfo) MatchReservationAffinity(reservationAffinity *reservationutil.RequiredReservationAffinity, nodeLabels map[string]string) bool {
+func (ri *ReservationInfo) MatchReservationAffinity(reservationAffinity *reservationutil.RequiredReservationAffinity, node *corev1.Node) bool {
 	if reservationAffinity != nil {
-		// NOTE: There are some special scenarios.
-		// For example, the AZ where the Pod wants to select the Reservation is cn-hangzhou, but the Reservation itself
-		// does not have this information, so it needs to perceive the label of the Node when Matching Affinity.
 		fakeNode := &corev1.Node{
 			ObjectMeta: metav1.ObjectMeta{
-				Name:   ri.GetName(),
-				Labels: map[string]string{},
+				Name: ri.GetName(),
 			},
 		}
-		for k, v := range nodeLabels {
-			fakeNode.Labels[k] = v
-		}
-		for k, v := range ri.GetObject().GetLabels() {
-			fakeNode.Labels[k] = v
+		reservationLabels := ri.GetObject().GetLabels()
+		if k8sfeature.DefaultFeatureGate.Enabled(features.OmitNodeLabelsForReservation) {
+			// In this case, we suppose the necessary node labels have been patched to the reservation.
+			fakeNode.Labels = reservationLabels
+		} else {
+			// NOTE: There are some special scenarios.
+			// For example, the AZ where the Pod wants to select the Reservation is cn-hangzhou, but the Reservation itself
+			// does not have this information, so it needs to perceive the label of the Node when Matching Affinity.
+			nodeLabels := node.GetLabels()
+			fakeNode.Labels = make(map[string]string, len(nodeLabels)+len(reservationLabels))
+			for k, v := range nodeLabels {
+				fakeNode.Labels[k] = v
+			}
+			for k, v := range reservationLabels {
+				fakeNode.Labels[k] = v
+			}
 		}
 		return reservationAffinity.MatchAffinity(fakeNode)
 	}
diff --git a/pkg/scheduler/plugins/elasticquota/plugin.go b/pkg/scheduler/plugins/elasticquota/plugin.go
index b7afbccc..3c1e6e00 100644
--- a/pkg/scheduler/plugins/elasticquota/plugin.go
+++ b/pkg/scheduler/plugins/elasticquota/plugin.go
@@ -359,3 +359,7 @@ func (g *Plugin) Unreserve(ctx context.Context, state *framework.CycleState, p *
 	}
 	mgr.UnreservePod(quotaName, p)
 }
+
+func (g *Plugin) GetQuotaInformer() cache.SharedIndexInformer { // expose for extensions
+	return g.quotaInformer
+}
diff --git a/pkg/scheduler/plugins/reservation/controller/controller.go b/pkg/scheduler/plugins/reservation/controller/controller.go
index e9681d25..e2ef976f 100644
--- a/pkg/scheduler/plugins/reservation/controller/controller.go
+++ b/pkg/scheduler/plugins/reservation/controller/controller.go
@@ -66,6 +66,7 @@ type Controller struct {
 	koordClientSet             koordclientset.Interface
 	queue                      workqueue.RateLimitingInterface
 	numWorker                  int
+	gcDuration                 time.Duration
 
 	lock sync.Mutex
 	pods map[string]map[types.UID]*corev1.Pod
@@ -88,6 +89,11 @@ func New(
 	if args != nil && args.ControllerWorkers > 0 {
 		numWorker = int(args.ControllerWorkers)
 	}
+
+	gcDuration := defaultGCDuration
+	if args != nil && args.GCDurationSeconds > 0 {
+		gcDuration = time.Duration(args.GCDurationSeconds) * time.Second
+	}
 	return &Controller{
 		sharedInformerFactory:      sharedInformerFactory,
 		koordSharedInformerFactory: koordSharedInformerFactory,
@@ -97,6 +103,7 @@ func New(
 		koordClientSet:             koordClientSet,
 		queue:                      queue,
 		numWorker:                  numWorker,
+		gcDuration:                 gcDuration,
 		pods:                       map[string]map[types.UID]*corev1.Pod{},
 	}
 }
diff --git a/pkg/scheduler/plugins/reservation/controller/garbage_collection.go b/pkg/scheduler/plugins/reservation/controller/garbage_collection.go
index 1959e1c9..4c3cd923 100644
--- a/pkg/scheduler/plugins/reservation/controller/garbage_collection.go
+++ b/pkg/scheduler/plugins/reservation/controller/garbage_collection.go
@@ -43,7 +43,7 @@ func (c *Controller) gcReservations() {
 	}
 	for _, reservation := range reservations {
 		if reservationutil.IsReservationExpired(reservation) || reservationutil.IsReservationSucceeded(reservation) {
-			if isReservationNeedCleanup(reservation) || missingNode(reservation, c.nodeLister) {
+			if isReservationNeedCleanup(reservation, c.gcDuration) || missingNode(reservation, c.nodeLister) {
 				if err = c.koordClientSet.SchedulingV1alpha1().Reservations().Delete(context.TODO(), reservation.Name, metav1.DeleteOptions{}); err != nil {
 					klog.V(3).InfoS("failed to delete reservation", "reservation", klog.KObj(reservation), "err", err)
 				} else {
@@ -65,20 +65,20 @@ func missingNode(reservation *schedulingv1alpha1.Reservation, nodeLister corelis
 	return false
 }
 
-func isReservationNeedCleanup(r *schedulingv1alpha1.Reservation) bool {
+func isReservationNeedCleanup(r *schedulingv1alpha1.Reservation, gcDuration time.Duration) bool {
 	if r == nil {
 		return true
 	}
 	if reservationutil.IsReservationExpired(r) {
 		for _, condition := range r.Status.Conditions {
 			if condition.Reason == schedulingv1alpha1.ReasonReservationExpired {
-				return time.Since(condition.LastTransitionTime.Time) > defaultGCDuration
+				return time.Since(condition.LastTransitionTime.Time) > gcDuration
 			}
 		}
 	} else if reservationutil.IsReservationSucceeded(r) {
 		for _, condition := range r.Status.Conditions {
 			if condition.Reason == schedulingv1alpha1.ReasonReservationSucceeded {
-				return time.Since(condition.LastProbeTime.Time) > defaultGCDuration
+				return time.Since(condition.LastProbeTime.Time) > gcDuration
 			}
 		}
 	}
diff --git a/pkg/scheduler/plugins/reservation/eventhandler.go b/pkg/scheduler/plugins/reservation/eventhandler.go
index 09f60866..14ad7b39 100644
--- a/pkg/scheduler/plugins/reservation/eventhandler.go
+++ b/pkg/scheduler/plugins/reservation/eventhandler.go
@@ -51,7 +51,8 @@ func (h *reservationEventHandler) OnAdd(obj interface{}, isInInitialList bool) {
 	}
 	if reservationutil.IsReservationActive(r) {
 		h.cache.updateReservation(r)
-		klog.V(4).InfoS("add reservation into reservationCache", "reservation", klog.KObj(r))
+		klog.V(4).InfoS("add reservation into reservationCache",
+			"reservation", klog.KObj(r), "node", reservationutil.GetReservationNodeName(r))
 	}
 }
 
@@ -65,11 +66,22 @@ func (h *reservationEventHandler) OnUpdate(oldObj, newObj interface{}) {
 		return
 	}
 
-	if reservationutil.IsReservationActive(newR) || reservationutil.IsReservationFailed(newR) || reservationutil.IsReservationSucceeded(newR) {
+	if reservationutil.IsReservationActive(newR) {
 		h.cache.updateReservation(newR)
 		podInfo, _ := framework.NewPodInfo(reservationutil.NewReservePod(newR))
 		h.rrNominator.DeleteReservePod(podInfo)
-		klog.V(4).InfoS("update reservation into reservationCache", "reservation", klog.KObj(newR))
+		klog.V(4).InfoS("update reservation into reservationCache",
+			"reservation", klog.KObj(newR), "node", reservationutil.GetReservationNodeName(newR))
+	} else if reservationutil.IsReservationFailed(newR) || reservationutil.IsReservationSucceeded(newR) {
+		// Here it is only marked that ReservationInfo is unavailable,
+		// and the real deletion operation is executed in deleteReservationFromCache(pkg/scheduler/frameworkext/eventhandlers/reservation_handler.go).
+		// This ensures that the Reserve Pod and the resources it holds are deleted correctly.
+		// NOTE: For the update event from available to terminated triggers the deleteReservationFromCache.
+		h.cache.updateReservationIfExists(newR)
+		klog.V(4).InfoS("update reservation into terminated so only update cache if exists",
+			"reservation", klog.KObj(newR), "node", reservationutil.GetReservationNodeName(newR))
+		podInfo, _ := framework.NewPodInfo(reservationutil.NewReservePod(newR))
+		h.rrNominator.DeleteReservePod(podInfo)
 	}
 }
 
@@ -95,10 +107,12 @@ func (h *reservationEventHandler) OnDelete(obj interface{}) {
 	// and the real deletion operation is executed in deleteReservationFromCache(pkg/scheduler/frameworkext/eventhandlers/reservation_handler.go).
 	// This ensures that the Reserve Pod and the resources it holds are deleted correctly.
 	if reservationutil.IsReservationAvailable(r) {
-		klog.V(4).InfoS("Reservation has been deleted but it's still available, mark it as Failed", "reservation", klog.KObj(r))
+		klog.V(4).InfoS("Reservation has been deleted but it's still available, mark it as Failed",
+			"reservation", klog.KObj(r), "node", reservationutil.GetReservationNodeName(r))
 		r = r.DeepCopy()
 		r.Status.Phase = schedulingv1alpha1.ReservationFailed
 	}
 	h.cache.updateReservationIfExists(r)
-	klog.V(4).InfoS("got delete reservation event but just update it if exists", "reservation", klog.KObj(r))
+	klog.V(4).InfoS("got delete reservation event but just update it if exists",
+		"reservation", klog.KObj(r), "node", reservationutil.GetReservationNodeName(r))
 }
diff --git a/pkg/scheduler/plugins/reservation/plugin.go b/pkg/scheduler/plugins/reservation/plugin.go
index a86dde6b..ae13c73d 100644
--- a/pkg/scheduler/plugins/reservation/plugin.go
+++ b/pkg/scheduler/plugins/reservation/plugin.go
@@ -42,6 +42,7 @@ import (
 	listerschedulingv1alpha1 "github.com/koordinator-sh/koordinator/pkg/client/listers/scheduling/v1alpha1"
 	"github.com/koordinator-sh/koordinator/pkg/features"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/apis/config"
+	"github.com/koordinator-sh/koordinator/pkg/scheduler/apis/config/validation"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/plugins/reservation/controller"
 	"github.com/koordinator-sh/koordinator/pkg/util"
@@ -100,6 +101,9 @@ func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error)
 	if !ok {
 		return nil, fmt.Errorf("want args to be of type ReservationArgs, got %T", args)
 	}
+	if err := validation.ValidateReservationArgs(nil, pluginArgs); err != nil {
+		return nil, err
+	}
 	extendedHandle, ok := handle.(frameworkext.ExtendedHandle)
 	if !ok {
 		return nil, fmt.Errorf("want handle to be of type frameworkext.ExtendedHandle, got %T", handle)
@@ -307,7 +311,7 @@ func (pl *Plugin) PreFilter(ctx context.Context, cycleState *framework.CycleStat
 			return nil, framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonReservationAffinity)
 		}
 		preResult = &framework.PreFilterResult{
-			NodeNames: sets.Set[string]{},
+			NodeNames: make(sets.Set[string], len(state.nodeReservationStates)),
 		}
 		for nodeName := range state.nodeReservationStates {
 			preResult.NodeNames.Insert(nodeName)
diff --git a/pkg/scheduler/plugins/reservation/transformer.go b/pkg/scheduler/plugins/reservation/transformer.go
index 17bf9be0..5dbf8cfa 100644
--- a/pkg/scheduler/plugins/reservation/transformer.go
+++ b/pkg/scheduler/plugins/reservation/transformer.go
@@ -133,7 +133,7 @@ func (pl *Plugin) prepareMatchReservationState(ctx context.Context, cycleState *
 				diagnosisState.taintsUnmatched++
 				taintKey := getDiagnosisTaintKey(&firstUnmatchedTaint)
 				diagnosisState.taintsUnmatchedReasons[taintKey]++
-			} else if !matchReservationAffinity(node, rInfo, reservationAffinity) { // ReservationAffinity unmatched
+			} else if !rInfo.MatchReservationAffinity(reservationAffinity, node) { // ReservationAffinity unmatched
 				diagnosisState.affinityUnmatched++
 			} else if !extension.ExactMatchReservation(podRequests, rInfo.Allocatable, exactMatchReservationSpec) { // exactMatchSpec unmatched
 				diagnosisState.notExactMatched++
@@ -543,33 +543,6 @@ func calculateResource(pod *corev1.Pod) (res framework.Resource, non0CPU int64,
 	return
 }
 
-// matchReservationAffinity returns the statuses of whether the reservation affinity matches, whether the reservation
-// taints are tolerated, and whether the reservation name matches.
-func matchReservationAffinity(node *corev1.Node, reservation *frameworkext.ReservationInfo, reservationAffinity *reservationutil.RequiredReservationAffinity) bool {
-	if reservationAffinity != nil {
-		// NOTE: There are some special scenarios.
-		// For example, the AZ where the Pod wants to select the Reservation is cn-hangzhou, but the Reservation itself
-		// does not have this information, so it needs to perceive the label of the Node when Matching Affinity.
-		// FIXME(saintube): clean up the default node labels casting and preserve optional labels
-		// https://github.com/koordinator-sh/koordinator/issues/2208
-		reservationLabels := reservation.GetObject().GetLabels()
-		fakeNode := &corev1.Node{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:   reservation.GetName(),
-				Labels: make(map[string]string, len(node.Labels)+len(reservationLabels)),
-			},
-		}
-		for k, v := range node.Labels {
-			fakeNode.Labels[k] = v
-		}
-		for k, v := range reservationLabels {
-			fakeNode.Labels[k] = v
-		}
-		return reservationAffinity.MatchAffinity(fakeNode)
-	}
-	return true
-}
-
 func parseSpecificNodesFromAffinity(pod *corev1.Pod) (sets.String, *framework.Status) {
 	affinity := pod.Spec.Affinity
 	if affinity == nil ||
diff --git a/pkg/util/hostport.go b/pkg/util/hostport.go
index 681c4420..9512473a 100644
--- a/pkg/util/hostport.go
+++ b/pkg/util/hostport.go
@@ -68,7 +68,7 @@ func CloneHostPorts(ports framework.HostPortInfo) framework.HostPortInfo {
 	if len(ports) == 0 {
 		return nil
 	}
-	r := framework.HostPortInfo{}
+	r := make(framework.HostPortInfo, len(ports))
 	for ip, protocolPorts := range ports {
 		for v := range protocolPorts {
 			r.Add(ip, v.Protocol, v.Port)



Test patch:
diff --git a/pkg/koordlet/metricsadvisor/collectors/resctrl/resctrl_collector_test.go b/pkg/koordlet/metricsadvisor/collectors/resctrl/resctrl_collector_test.go
new file mode 100644
index 00000000..506fd5bb
--- /dev/null
+++ b/pkg/koordlet/metricsadvisor/collectors/resctrl/resctrl_collector_test.go
@@ -0,0 +1,167 @@
+/*
+Copyright 2022 The Koordinator Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package resctrl
+
+import (
+	"testing"
+
+	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/metriccache"
+	mockmetriccache "github.com/koordinator-sh/koordinator/pkg/koordlet/metriccache/mockmetriccache"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/metricsadvisor/framework"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/resourceexecutor"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/statesinformer"
+	mockstatesinformer "github.com/koordinator-sh/koordinator/pkg/koordlet/statesinformer/mockstatesinformer"
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/util/system"
+)
+
+func TestNewResctrlCollector(t *testing.T) {
+	type args struct {
+		cfg            *framework.Config
+		statesInformer statesinformer.StatesInformer
+		metricCache    metriccache.MetricCache
+	}
+	tests := []struct {
+		name string
+		args args
+	}{
+		{
+			name: "new-resctrl-collector",
+			args: args{
+				cfg:            framework.NewDefaultConfig(),
+				statesInformer: nil,
+				metricCache:    nil,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			opt := &framework.Options{
+				Config:         tt.args.cfg,
+				StatesInformer: tt.args.statesInformer,
+				MetricCache:    tt.args.metricCache,
+			}
+			if got := New(opt); got == nil {
+				t.Errorf("NewResctrlCollector() = %v", got)
+			}
+		})
+	}
+}
+
+func Test_collectQosResctrlStatNoErr(t *testing.T) {
+	type args struct {
+		platformFlags string // platformFlags is the platform flags for the resctrl
+	}
+
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+	mockStatesInformer := mockstatesinformer.NewMockStatesInformer(ctrl)
+	mockMetricCache := mockmetriccache.NewMockMetricCache(ctrl)
+	mockStatesInformer.EXPECT().GetAllPods().Return([]*statesinformer.PodMeta{}).AnyTimes()
+	mockStatesInformer.EXPECT().HasSynced().Return(true).AnyTimes()
+	appender := mockmetriccache.NewMockAppender(ctrl)
+	mockMetricCache.EXPECT().Appender().Return(appender).AnyTimes()
+	appender.EXPECT().Append(gomock.Any()).Return(nil).AnyTimes()
+	appender.EXPECT().Commit().Return(nil).AnyTimes()
+
+	// create mock resctrl file system data
+	mmds := map[string]system.MockMonData{
+		"": {
+			CacheItems: map[int]system.MockCacheItem{
+				0: {
+					"llc_occupancy":   1,
+					"mbm_local_bytes": 2,
+					"mbm_total_bytes": 3,
+				},
+				1: {
+					"llc_occupancy":   4,
+					"mbm_local_bytes": 5,
+					"mbm_total_bytes": 6,
+				},
+			},
+		},
+		"BE": {
+			CacheItems: map[int]system.MockCacheItem{
+				0: {
+					"llc_occupancy":   11,
+					"mbm_local_bytes": 21,
+					"mbm_total_bytes": 31,
+				},
+				1: {
+					"llc_occupancy":   41,
+					"mbm_local_bytes": 51,
+					"mbm_total_bytes": 61,
+				},
+			},
+		},
+	}
+
+	platformTest := []struct {
+		args    args
+		wantErr bool
+	}{
+		{
+			args: args{
+				platformFlags: "vendor_id       : GenuineIntel\nflags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 invpcid_single intel_ppin ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect wbnoinvd dtherm ida arat pln pts avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid fsrm md_clear pconfig flush_l1d arch_capabilities",
+			},
+			wantErr: false,
+		},
+		{
+			args: args{
+				platformFlags: "vendor_id       : AuthenticAMD\nflags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc extd_apicid pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs xop skinit wdt lwp fma4 tce nodeid_msr tbm topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb hw_pstate ssbd ibpb vmmcall fsgsbase bmi1 avx2 smep bmi2 rdseed adx smap clflushopt sha_ni xsaveopt xsavec xgetbv1 xsaves clzero irperf xsaveerptr arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif overflow_recov succor smca",
+			},
+			wantErr: false,
+		},
+		{
+			args: args{
+				platformFlags: "",
+			},
+			wantErr: false,
+		},
+	}
+
+	for _, test := range platformTest {
+		t.Run("test collect qos resctrl stat", func(t *testing.T) {
+			helper := system.NewFileTestUtil(t)
+			defer helper.Cleanup()
+			helper.WriteProcSubFileContents("cpuinfo", test.args.platformFlags)
+			for ctrlGrp, mmd := range mmds {
+				system.TestingPrepareResctrlMondata(t, system.Conf.SysFSRootDir, ctrlGrp, mmd)
+			}
+
+			collector := New(&framework.Options{
+				Config:         framework.NewDefaultConfig(),
+				StatesInformer: mockStatesInformer,
+				MetricCache:    mockMetricCache,
+				CgroupReader:   resourceexecutor.NewCgroupReader(),
+			})
+
+			c := collector.(*resctrlCollector)
+			if !test.wantErr {
+				assert.NotPanics(t, func() {
+					c.collectQoSResctrlStat()
+				})
+			} else {
+				assert.Panics(t, func() {
+					c.collectQoSResctrlStat()
+				})
+			}
+		})
+	}
+}
diff --git a/pkg/koordlet/metricsadvisor/framework/config_test.go b/pkg/koordlet/metricsadvisor/framework/config_test.go
index 29790541..7ff59085 100644
--- a/pkg/koordlet/metricsadvisor/framework/config_test.go
+++ b/pkg/koordlet/metricsadvisor/framework/config_test.go
@@ -34,6 +34,7 @@ func Test_NewDefaultConfig(t *testing.T) {
 		PSICollectorInterval:             10 * time.Second,
 		CPICollectorTimeWindow:           10 * time.Second,
 		ColdPageCollectorInterval:        5 * time.Second,
+		ResctrlCollectorInterval:         10 * time.Second,
 		EnablePageCacheCollector:         false,
 	}
 	defaultConfig := NewDefaultConfig()
@@ -51,6 +52,7 @@ func Test_InitFlags(t *testing.T) {
 		"--psi-collector-interval=5s",
 		"--collect-cpi-timewindow=15s",
 		"--coldpage-collector-interval=15s",
+		"--resctrl-collector-interval=90s",
 	}
 	fs := flag.NewFlagSet(cmdArgs[0], flag.ExitOnError)
 
@@ -63,6 +65,7 @@ func Test_InitFlags(t *testing.T) {
 		PSICollectorInterval             time.Duration
 		CPICollectorTimeWindow           time.Duration
 		ColdPageCollectorInterval        time.Duration
+		ResctrlCollectorInterval         time.Duration
 	}
 	type args struct {
 		fs *flag.FlagSet
@@ -83,6 +86,7 @@ func Test_InitFlags(t *testing.T) {
 				PSICollectorInterval:             5 * time.Second,
 				CPICollectorTimeWindow:           15 * time.Second,
 				ColdPageCollectorInterval:        15 * time.Second,
+				ResctrlCollectorInterval:         90 * time.Second,
 			},
 			args: args{fs: fs},
 		},
@@ -98,6 +102,7 @@ func Test_InitFlags(t *testing.T) {
 				PSICollectorInterval:             tt.fields.PSICollectorInterval,
 				CPICollectorTimeWindow:           tt.fields.CPICollectorTimeWindow,
 				ColdPageCollectorInterval:        tt.fields.ColdPageCollectorInterval,
+				ResctrlCollectorInterval:         tt.fields.ResctrlCollectorInterval,
 			}
 			c := NewDefaultConfig()
 			c.InitFlags(tt.args.fs)
diff --git a/pkg/koordlet/resourceexecutor/resctrl_test.go b/pkg/koordlet/resourceexecutor/resctrl_test.go
new file mode 100644
index 00000000..06630f11
--- /dev/null
+++ b/pkg/koordlet/resourceexecutor/resctrl_test.go
@@ -0,0 +1,301 @@
+/*
+Copyright 2022 The Koordinator Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package resourceexecutor
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/koordinator-sh/koordinator/pkg/koordlet/util/system"
+)
+
+func TestNewResctrlReader(t *testing.T) {
+	type args struct {
+		content string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    reflect.Type
+		wantErr bool
+	}{
+		{
+			name: "test amd",
+			args: args{
+				content: "vendor_id       : AuthenticAMD\n",
+			},
+			want:    reflect.TypeOf(&ResctrlAMDReader{}),
+			wantErr: false,
+		},
+		{
+			name: "test intel",
+			args: args{
+				content: "vendor_id       : GenuineIntel",
+			},
+			want:    reflect.TypeOf(&ResctrlRDTReader{}),
+			wantErr: false,
+		},
+		{
+			name: "test arm",
+			args: args{
+				content: "vendor_id       : arm",
+			},
+			want:    reflect.TypeOf(&fakeReader{}),
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			helper := system.NewFileTestUtil(t)
+			helper.WriteProcSubFileContents("cpuinfo", tt.args.content)
+
+			rr := NewResctrlReader()
+			assert.Equal(t, tt.want, reflect.TypeOf(rr))
+		})
+	}
+}
+
+// just for x86 system
+func TestResctrlReader(t *testing.T) {
+	type args struct {
+		mmds map[string]system.MockMonData
+		qos  string
+	}
+
+	// llc result
+	llcTests := []struct {
+		name    string
+		args    args
+		want    map[CacheId]uint64
+		wantErr bool
+	}{
+		{
+			name: "root resctrl get llc result",
+			args: args{
+				mmds: map[string]system.MockMonData{
+					"": {
+						CacheItems: map[int]system.MockCacheItem{
+							0: {
+								"llc_occupancy":   1,
+								"mbm_local_bytes": 2,
+								"mbm_total_bytes": 3,
+							},
+							1: {
+								"llc_occupancy":   4,
+								"mbm_local_bytes": 5,
+								"mbm_total_bytes": 6,
+							},
+						},
+					},
+				},
+				qos: "",
+			},
+			want: map[CacheId]uint64{
+				0: 1,
+				1: 4,
+			},
+			wantErr: false,
+		},
+		{
+			name: "be qos get llc result",
+			args: args{
+				mmds: map[string]system.MockMonData{
+					"": {
+						CacheItems: map[int]system.MockCacheItem{
+							0: {
+								"llc_occupancy":   1,
+								"mbm_local_bytes": 2,
+								"mbm_total_bytes": 3,
+							},
+							1: {
+								"llc_occupancy":   4,
+								"mbm_local_bytes": 5,
+								"mbm_total_bytes": 6,
+							},
+						},
+					},
+					"BE": {
+						CacheItems: map[int]system.MockCacheItem{
+							0: {
+								"llc_occupancy":   11,
+								"mbm_local_bytes": 21,
+								"mbm_total_bytes": 31,
+							},
+							1: {
+								"llc_occupancy":   41,
+								"mbm_local_bytes": 51,
+								"mbm_total_bytes": 61,
+							},
+						},
+					},
+				},
+				qos: "BE",
+			},
+			want: map[CacheId]uint64{
+				0: 11,
+				1: 41,
+			},
+			wantErr: false,
+		},
+	}
+	for _, test := range llcTests {
+		t.Run("test resctrl llc collect", func(t *testing.T) {
+			helper := system.NewFileTestUtil(t)
+			defer helper.Cleanup()
+
+			// add cpuinfo for x86 cpuid
+			helper.WriteProcSubFileContents("cpuinfo", "vendor_id       : GenuineIntel\nflags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 invpcid_single intel_ppin ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect wbnoinvd dtherm ida arat pln pts avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid fsrm md_clear pconfig flush_l1d arch_capabilities")
+
+			for ctrlGrp, mmd := range test.args.mmds {
+				system.TestingPrepareResctrlMondata(t, system.Conf.SysFSRootDir, ctrlGrp, mmd)
+			}
+
+			data, err := NewResctrlReader().ReadResctrlL3Stat(test.args.qos)
+			assert.NoError(t, err)
+
+			for cacheId, value := range data {
+				assert.Equal(t, test.want[cacheId], value)
+			}
+		})
+	}
+
+	mbTests := []struct {
+		name    string
+		args    args
+		want    map[CacheId]system.MBStatData
+		wantErr bool
+	}{
+		{
+			name: "root resctrl get llc result",
+			args: args{
+				mmds: map[string]system.MockMonData{
+					"": {
+						CacheItems: map[int]system.MockCacheItem{
+							0: {
+								"llc_occupancy":   1,
+								"mbm_local_bytes": 2,
+								"mbm_total_bytes": 3,
+							},
+							1: {
+								"llc_occupancy":   4,
+								"mbm_local_bytes": 5,
+								"mbm_total_bytes": 6,
+							},
+						},
+					},
+				},
+				qos: "",
+			},
+			want: map[CacheId]system.MBStatData{
+				0: {
+					"mbm_local_bytes": 2,
+					"mbm_total_bytes": 3,
+				},
+				1: {
+					"mbm_local_bytes": 5,
+					"mbm_total_bytes": 6,
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name: "be qos get llc result",
+			args: args{
+				mmds: map[string]system.MockMonData{
+					"": {
+						CacheItems: map[int]system.MockCacheItem{
+							0: {
+								"llc_occupancy":   1,
+								"mbm_local_bytes": 2,
+								"mbm_total_bytes": 3,
+							},
+							1: {
+								"llc_occupancy":   4,
+								"mbm_local_bytes": 5,
+								"mbm_total_bytes": 6,
+							},
+						},
+					},
+					"BE": {
+						CacheItems: map[int]system.MockCacheItem{
+							0: {
+								"llc_occupancy":   11,
+								"mbm_local_bytes": 21,
+								"mbm_total_bytes": 31,
+							},
+							1: {
+								"llc_occupancy":   41,
+								"mbm_local_bytes": 51,
+								"mbm_total_bytes": 61,
+							},
+						},
+					},
+				},
+				qos: "BE",
+			},
+			want: map[CacheId]system.MBStatData{
+				0: {
+					"mbm_local_bytes": 21,
+					"mbm_total_bytes": 31,
+				},
+				1: {
+					"mbm_local_bytes": 51,
+					"mbm_total_bytes": 61,
+				},
+			},
+			wantErr: false,
+		},
+	}
+	for _, test := range mbTests {
+		t.Run("test resctrl mb collect", func(t *testing.T) {
+			helper := system.NewFileTestUtil(t)
+			defer helper.Cleanup()
+
+			// add cpuinfo for intel cpuid
+			helper.WriteProcSubFileContents("cpuinfo", "vendor_id       : GenuineIntel\nflags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 invpcid_single intel_ppin ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect wbnoinvd dtherm ida arat pln pts avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid fsrm md_clear pconfig flush_l1d arch_capabilities")
+
+			for ctrlGrp, mmd := range test.args.mmds {
+				system.TestingPrepareResctrlMondata(t, system.Conf.SysFSRootDir, ctrlGrp, mmd)
+			}
+
+			data, err := NewResctrlReader().ReadResctrlMBStat(test.args.qos)
+			assert.NoError(t, err)
+
+			for cacheId, value := range data {
+				assert.Equal(t, test.want[cacheId], value)
+			}
+		})
+	}
+}
+
+func TestFakeReader(t *testing.T) {
+	t.Run("test fake reader ", func(t *testing.T) {
+		helper := system.NewFileTestUtil(t)
+		defer helper.Cleanup()
+		reader := NewResctrlReader()
+
+		l3Data, err := reader.ReadResctrlL3Stat("")
+		assert.Nil(t, l3Data)
+		assert.Error(t, err)
+
+		mbmData, err := reader.ReadResctrlMBStat("")
+		assert.Nil(t, mbmData)
+		assert.Error(t, err)
+	})
+}
diff --git a/pkg/koordlet/util/system/resctrl_test.go b/pkg/koordlet/util/system/resctrl_test.go
index 63c45158..3b5bb4d3 100644
--- a/pkg/koordlet/util/system/resctrl_test.go
+++ b/pkg/koordlet/util/system/resctrl_test.go
@@ -425,3 +425,169 @@ func Test_isResctrlAvailableByKernelCmd(t *testing.T) {
 		})
 	}
 }
+
+func Test_isResctrlMBMAvailableByCpuInfo(t *testing.T) {
+	type field struct {
+		cpuInfoContents string
+	}
+
+	tests := []struct {
+		name           string
+		field          field
+		expectIsMBMSet bool
+	}{
+		{
+			name: "testMBMDisable",
+			field: field{
+				cpuInfoContents: "flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid tsc_known_freq pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single ibrs_enhanced tsc_adjust bmi1 avx2 smep bmi2 erms invpcid avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves wbnoinvd arat avx512vbmi pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid fsrm arch_capabilities",
+			},
+			expectIsMBMSet: false,
+		},
+		{
+			name: "testMBMEnable",
+			field: field{
+				cpuInfoContents: "flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 invpcid_single intel_ppin ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect wbnoinvd dtherm ida arat pln pts avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid fsrm md_clear pconfig flush_l1d arch_capabilities",
+			},
+			expectIsMBMSet: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			helper := NewFileTestUtil(t)
+			defer helper.Cleanup()
+			helper.WriteProcSubFileContents("cpuinfo", tt.field.cpuInfoContents)
+
+			isMBMAvailable, err := isResctrlMBMAvailableByCpuInfo(GetCPUInfoPath())
+			assert.NoError(t, err)
+			assert.Equal(t, tt.expectIsMBMSet, isMBMAvailable)
+		})
+	}
+}
+
+func Test_isResctrlCQMAvailableByCpuInfo(t *testing.T) {
+	type field struct {
+		cpuInfoContents string
+	}
+
+	tests := []struct {
+		name           string
+		field          field
+		expectIsCQMSet bool
+	}{
+		{
+			name: "testMBMDisable",
+			field: field{
+				cpuInfoContents: "flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid tsc_known_freq pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single ibrs_enhanced tsc_adjust bmi1 avx2 smep bmi2 erms invpcid avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves wbnoinvd arat avx512vbmi pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid fsrm arch_capabilities",
+			},
+			expectIsCQMSet: false,
+		},
+		{
+			name: "testMBMEnable",
+			field: field{
+				cpuInfoContents: "flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 invpcid_single intel_ppin ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect wbnoinvd dtherm ida arat pln pts avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid fsrm md_clear pconfig flush_l1d arch_capabilities",
+			},
+			expectIsCQMSet: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			helper := NewFileTestUtil(t)
+			defer helper.Cleanup()
+			helper.WriteProcSubFileContents("cpuinfo", tt.field.cpuInfoContents)
+
+			isMBMAvailable, err := isResctrlCQMAvailableByCpuInfo(GetCPUInfoPath())
+			assert.NoError(t, err)
+			assert.Equal(t, tt.expectIsCQMSet, isMBMAvailable)
+		})
+	}
+}
+
+func TestGetResctrlMonDataPath(t *testing.T) {
+	type args struct {
+		groupPath string
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			name: "testResctrlMonDataPath",
+			want: "/resctrl/mon_data",
+			args: args{groupPath: ""},
+		},
+		{
+			name: "testResctrlMonDataPath_QoS",
+			want: "/resctrl/BE/mon_data",
+			args: args{groupPath: "BE"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			helper := NewFileTestUtil(t)
+			defer helper.Cleanup()
+			Conf = &Config{
+				SysFSRootDir: helper.TempDir,
+			}
+			got := GetResctrlMonDataPath(tt.args.groupPath)
+
+			assert.Equal(t, filepath.Join(Conf.SysFSRootDir, tt.want), got)
+		})
+	}
+}
+
+func TestIsVendorSupportResctrl(t *testing.T) {
+	// use HiSilicon for example
+	ARM_VENDOR_ID_MAP["HiSilicon"] = struct{}{}
+	type args struct {
+		vendorID string
+	}
+	tests := []struct {
+		name string
+		args args
+		want bool
+	}{
+		{
+			name: "intel",
+			args: args{
+				vendorID: "GenuineIntel",
+			},
+			want: true,
+		},
+		{
+			name: "AMD",
+			args: args{
+				vendorID: "AuthenticAMD",
+			},
+			want: true,
+		},
+		{
+			name: "HiSilicon",
+			args: args{
+				vendorID: "HiSilicon",
+			},
+			want: true,
+		},
+		{
+			name: "NVIDIA",
+			args: args{
+				vendorID: "NVIDIA",
+			},
+			want: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			helper := NewFileTestUtil(t)
+			defer helper.Cleanup()
+			p := GetCPUInfoPath()
+			helper.WriteFileContents(p, "vendor_id       : "+tt.args.vendorID)
+
+			got := IsVendorSupportResctrl()
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
diff --git a/pkg/koordlet/util/system/util_test_tool.go b/pkg/koordlet/util/system/util_test_tool.go
index a79c05b7..a9729ccc 100644
--- a/pkg/koordlet/util/system/util_test_tool.go
+++ b/pkg/koordlet/util/system/util_test_tool.go
@@ -65,6 +65,29 @@ type FileTestUtil struct {
 	t testing.TB
 }
 
+type MockMonData struct {
+	CacheItems map[int]MockCacheItem
+}
+
+type MockCacheItem map[string]uint64
+
+// create mock ctrl group mon_data directory and files
+func TestingPrepareResctrlMondata(t *testing.T, sysFsRootPath, ctrlGrp string, mmd MockMonData) {
+	resctrlMonDataPath := filepath.Join(sysFsRootPath, "resctrl", ctrlGrp, "mon_data")
+	err := os.MkdirAll(resctrlMonDataPath, 0777)
+	assert.NoError(t, err)
+	for cacheId, cacheItem := range mmd.CacheItems {
+		cacheMonDir := filepath.Join(resctrlMonDataPath, fmt.Sprintf("mon_L3_%02d", cacheId))
+		err = os.MkdirAll(cacheMonDir, 0777)
+		assert.NoError(t, err)
+		for item, value := range cacheItem {
+			itemPath := filepath.Join(cacheMonDir, item)
+			err = os.WriteFile(itemPath, []byte(fmt.Sprintf("%d", value)), 0644)
+			assert.NoError(t, err)
+		}
+	}
+}
+
 // NewFileTestUtil creates a new test util for the specified subsystem.
 // NOTE: this function should be called only for testing purposes.
 func NewFileTestUtil(t testing.TB) *FileTestUtil {
diff --git a/pkg/koordlet/util/system/util_test_tool_test.go b/pkg/koordlet/util/system/util_test_tool_test.go
index 8e65f1ea..3fc44f99 100644
--- a/pkg/koordlet/util/system/util_test_tool_test.go
+++ b/pkg/koordlet/util/system/util_test_tool_test.go
@@ -17,7 +17,10 @@ limitations under the License.
 package system
 
 import (
+	"fmt"
 	"path"
+	"path/filepath"
+	"strconv"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -68,3 +71,56 @@ func Test_ProcFileFuncs(t *testing.T) {
 	assert.Equal(t, "testContents", gotContents, "testReadProcSubFileContents")
 
 }
+
+func Test_TestingPrepareResctrlMondata(t *testing.T) {
+	helper := NewFileTestUtil(t)
+	defer helper.Cleanup()
+
+	mmds := map[string]MockMonData{
+		"": {
+			CacheItems: map[int]MockCacheItem{
+				0: {
+					"llc_occupancy":   1,
+					"mbm_local_bytes": 2,
+					"mbm_total_bytes": 3,
+				},
+				1: {
+					"llc_occupancy":   4,
+					"mbm_local_bytes": 5,
+					"mbm_total_bytes": 6,
+				},
+			},
+		},
+		"BE": {
+			CacheItems: map[int]MockCacheItem{
+				0: {
+					"llc_occupancy":   11,
+					"mbm_local_bytes": 21,
+					"mbm_total_bytes": 31,
+				},
+				1: {
+					"llc_occupancy":   41,
+					"mbm_local_bytes": 51,
+					"mbm_total_bytes": 61,
+				},
+			},
+		},
+	}
+
+	for ctrlGrp, mmd := range mmds {
+		TestingPrepareResctrlMondata(t, Conf.SysFSRootDir, ctrlGrp, mmd)
+	}
+
+	for ctrlGrp, mmd := range mmds {
+		for cacheId, cacheItem := range mmd.CacheItems {
+			for item, value := range cacheItem {
+				itemPath := filepath.Join(Conf.SysFSRootDir, "resctrl", ctrlGrp,
+					"mon_data", fmt.Sprintf("mon_L3_%02d", cacheId), item)
+				gotContents := helper.ReadFileContents(itemPath)
+				data, err := strconv.Atoi(gotContents)
+				assert.NoError(t, err)
+				assert.Equal(t, value, uint64(data), "testReadFileContents")
+			}
+		}
+	}
+}
diff --git a/pkg/scheduler/frameworkext/reservation_info_test.go b/pkg/scheduler/frameworkext/reservation_info_test.go
index 07c90992..65b6c988 100644
--- a/pkg/scheduler/frameworkext/reservation_info_test.go
+++ b/pkg/scheduler/frameworkext/reservation_info_test.go
@@ -17,6 +17,7 @@ limitations under the License.
 package frameworkext
 
 import (
+	"encoding/json"
 	"sort"
 	"testing"
 	"time"
@@ -733,6 +734,7 @@ func TestReservationInfoRefreshAvailable(t *testing.T) {
 						},
 					},
 				},
+
 				Owners: []schedulingv1alpha1.ReservationOwner{
 					{
 						LabelSelector: &metav1.LabelSelector{
@@ -743,6 +745,7 @@ func TestReservationInfoRefreshAvailable(t *testing.T) {
 					},
 				},
 			},
+
 			Status: schedulingv1alpha1.ReservationStatus{
 				NodeName:    "test-node",
 				Phase:       schedulingv1alpha1.ReasonReservationAvailable,
@@ -792,3 +795,126 @@ func TestReservationInfoRefreshAvailable(t *testing.T) {
 		assert.Equal(t, expectedRInfo, rInfo)
 	})
 }
+
+func TestReservationInfoMatchReservationAffinity(t *testing.T) {
+	tests := []struct {
+		name                string
+		reservation         *schedulingv1alpha1.Reservation
+		reservationAffinity *apiext.ReservationAffinity
+		want                bool
+	}{
+		{
+			name: "nothing to match",
+			reservation: &schedulingv1alpha1.Reservation{
+				Spec: schedulingv1alpha1.ReservationSpec{
+					Owners: []schedulingv1alpha1.ReservationOwner{
+						{
+							LabelSelector: &metav1.LabelSelector{
+								MatchLabels: map[string]string{
+									"app": "test",
+								},
+							},
+						},
+					},
+				},
+			},
+			want: true,
+		},
+		{
+			name: "match reservation affinity",
+			reservation: &schedulingv1alpha1.Reservation{
+				ObjectMeta: metav1.ObjectMeta{
+					Labels: map[string]string{
+						"reservation-type": "reservation-test",
+					},
+				},
+				Spec: schedulingv1alpha1.ReservationSpec{
+					Owners: []schedulingv1alpha1.ReservationOwner{
+						{
+							LabelSelector: &metav1.LabelSelector{
+								MatchLabels: map[string]string{
+									"app": "test",
+								},
+							},
+						},
+					},
+				},
+			},
+			reservationAffinity: &apiext.ReservationAffinity{
+				RequiredDuringSchedulingIgnoredDuringExecution: &apiext.ReservationAffinitySelector{
+					ReservationSelectorTerms: []corev1.NodeSelectorTerm{
+						{
+							MatchExpressions: []corev1.NodeSelectorRequirement{
+								{
+									Key:      "reservation-type",
+									Operator: corev1.NodeSelectorOpIn,
+									Values:   []string{"reservation-test"},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: true,
+		},
+		{
+			name: "not match reservation affinity",
+			reservation: &schedulingv1alpha1.Reservation{
+				ObjectMeta: metav1.ObjectMeta{
+					Labels: map[string]string{
+						"reservation-type": "reservation-test-not-match",
+					},
+				},
+				Spec: schedulingv1alpha1.ReservationSpec{
+					Owners: []schedulingv1alpha1.ReservationOwner{
+						{
+							LabelSelector: &metav1.LabelSelector{
+								MatchLabels: map[string]string{
+									"app": "test",
+								},
+							},
+						},
+					},
+				},
+			},
+			reservationAffinity: &apiext.ReservationAffinity{
+				RequiredDuringSchedulingIgnoredDuringExecution: &apiext.ReservationAffinitySelector{
+					ReservationSelectorTerms: []corev1.NodeSelectorTerm{
+						{
+							MatchExpressions: []corev1.NodeSelectorRequirement{
+								{
+									Key:      "reservation-type",
+									Operator: corev1.NodeSelectorOpIn,
+									Values:   []string{"reservation-test"},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			pod := &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "test-pod",
+				},
+			}
+			if tt.reservationAffinity != nil {
+				affinityData, err := json.Marshal(tt.reservationAffinity)
+				assert.NoError(t, err)
+				if pod.Annotations == nil {
+					pod.Annotations = map[string]string{}
+				}
+				pod.Annotations[apiext.AnnotationReservationAffinity] = string(affinityData)
+			}
+			reservationAffinity, err := reservationutil.GetRequiredReservationAffinity(pod)
+			assert.NoError(t, err)
+			rInfo := NewReservationInfo(tt.reservation)
+			got := rInfo.MatchReservationAffinity(reservationAffinity, &corev1.Node{})
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
diff --git a/pkg/scheduler/plugins/reservation/controller/controller_test.go b/pkg/scheduler/plugins/reservation/controller/controller_test.go
index 50b531c1..0c01acee 100644
--- a/pkg/scheduler/plugins/reservation/controller/controller_test.go
+++ b/pkg/scheduler/plugins/reservation/controller/controller_test.go
@@ -293,7 +293,7 @@ func TestSyncStatus(t *testing.T) {
 		assert.NoError(t, err)
 	}
 
-	controller := New(sharedInformerFactory, koordSharedInformerFactory, fakeKoordClientSet, &config.ReservationArgs{})
+	controller := New(sharedInformerFactory, koordSharedInformerFactory, fakeKoordClientSet, &config.ReservationArgs{ControllerWorkers: 2, GCDurationSeconds: 3600})
 	controller.Start()
 
 	time.Sleep(1 * time.Second)
diff --git a/pkg/scheduler/plugins/reservation/controller/garbage_collection_test.go b/pkg/scheduler/plugins/reservation/controller/garbage_collection_test.go
index 0b465f88..ce24c486 100644
--- a/pkg/scheduler/plugins/reservation/controller/garbage_collection_test.go
+++ b/pkg/scheduler/plugins/reservation/controller/garbage_collection_test.go
@@ -58,6 +58,24 @@ func TestGC(t *testing.T) {
 			NodeName: "test-node",
 		},
 	}
+	succeededReservation := &schedulingv1alpha1.Reservation{
+		ObjectMeta: metav1.ObjectMeta{
+			UID:  uuid.NewUUID(),
+			Name: "succededReservation",
+		},
+		Status: schedulingv1alpha1.ReservationStatus{
+			Phase: schedulingv1alpha1.ReservationSucceeded,
+			Conditions: []schedulingv1alpha1.ReservationCondition{
+				{
+					Type:               schedulingv1alpha1.ReservationConditionReady,
+					Status:             schedulingv1alpha1.ConditionStatusFalse,
+					Reason:             schedulingv1alpha1.ReasonReservationSucceeded,
+					LastProbeTime:      metav1.Time{Time: metav1.Now().Add(-48 * time.Hour)},
+					LastTransitionTime: metav1.Time{Time: metav1.Now().Add(-48 * time.Hour)},
+				},
+			},
+		},
+	}
 	normalReservation := &schedulingv1alpha1.Reservation{
 		ObjectMeta: metav1.ObjectMeta{
 			UID:               uuid.NewUUID(),
@@ -87,6 +105,7 @@ func TestGC(t *testing.T) {
 
 	reservations := []*schedulingv1alpha1.Reservation{
 		shouldExpireReservation,
+		succeededReservation,
 		normalReservation,
 		missingNodeReservation,
 	}
diff --git a/pkg/scheduler/plugins/reservation/eventhandler_test.go b/pkg/scheduler/plugins/reservation/eventhandler_test.go
index 51e419bc..9cc4f424 100644
--- a/pkg/scheduler/plugins/reservation/eventhandler_test.go
+++ b/pkg/scheduler/plugins/reservation/eventhandler_test.go
@@ -184,25 +184,28 @@ func TestEventHandlerUpdate(t *testing.T) {
 			name:            "pending to failed",
 			oldReservation:  pendingReservation,
 			newReservation:  failedReservation,
-			wantReservation: failedReservation,
+			wantReservation: nil,
 		},
 		{
 			name:            "pending to succeeded",
 			oldReservation:  pendingReservation,
 			newReservation:  succeededReservation,
-			wantReservation: succeededReservation,
+			wantReservation: nil,
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			cache := newReservationCache(nil)
 			eh := &reservationEventHandler{cache: cache, rrNominator: newNominator(nil, nil)}
+			eh.OnAdd(tt.oldReservation, false)
+
 			eh.OnUpdate(tt.oldReservation, tt.newReservation)
 			if tt.wantReservation == nil {
 				rInfo := cache.getReservationInfoByUID(tt.newReservation.UID)
 				assert.Nil(t, rInfo)
 			} else {
 				rInfo := cache.getReservationInfoByUID(tt.wantReservation.UID)
+				assert.NotNil(t, rInfo)
 				assert.Equal(t, tt.wantReservation, rInfo.Reservation)
 			}
 		})
diff --git a/pkg/scheduler/plugins/reservation/transformer_test.go b/pkg/scheduler/plugins/reservation/transformer_test.go
index 4127dad6..75ed699a 100644
--- a/pkg/scheduler/plugins/reservation/transformer_test.go
+++ b/pkg/scheduler/plugins/reservation/transformer_test.go
@@ -18,7 +18,6 @@ package reservation
 
 import (
 	"context"
-	"encoding/json"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -727,129 +726,6 @@ func TestRestoreReservationWithLazyReservationRestore(t *testing.T) {
 	assert.True(t, equality.Semantic.DeepEqual(expectNodeInfo, nodeInfo))
 }
 
-func Test_matchReservation(t *testing.T) {
-	tests := []struct {
-		name                string
-		reservation         *schedulingv1alpha1.Reservation
-		reservationAffinity *apiext.ReservationAffinity
-		want                bool
-	}{
-		{
-			name: "nothing to match",
-			reservation: &schedulingv1alpha1.Reservation{
-				Spec: schedulingv1alpha1.ReservationSpec{
-					Owners: []schedulingv1alpha1.ReservationOwner{
-						{
-							LabelSelector: &metav1.LabelSelector{
-								MatchLabels: map[string]string{
-									"app": "test",
-								},
-							},
-						},
-					},
-				},
-			},
-			want: true,
-		},
-		{
-			name: "match reservation affinity",
-			reservation: &schedulingv1alpha1.Reservation{
-				ObjectMeta: metav1.ObjectMeta{
-					Labels: map[string]string{
-						"reservation-type": "reservation-test",
-					},
-				},
-				Spec: schedulingv1alpha1.ReservationSpec{
-					Owners: []schedulingv1alpha1.ReservationOwner{
-						{
-							LabelSelector: &metav1.LabelSelector{
-								MatchLabels: map[string]string{
-									"app": "test",
-								},
-							},
-						},
-					},
-				},
-			},
-			reservationAffinity: &apiext.ReservationAffinity{
-				RequiredDuringSchedulingIgnoredDuringExecution: &apiext.ReservationAffinitySelector{
-					ReservationSelectorTerms: []corev1.NodeSelectorTerm{
-						{
-							MatchExpressions: []corev1.NodeSelectorRequirement{
-								{
-									Key:      "reservation-type",
-									Operator: corev1.NodeSelectorOpIn,
-									Values:   []string{"reservation-test"},
-								},
-							},
-						},
-					},
-				},
-			},
-			want: true,
-		},
-		{
-			name: "not match reservation affinity",
-			reservation: &schedulingv1alpha1.Reservation{
-				ObjectMeta: metav1.ObjectMeta{
-					Labels: map[string]string{
-						"reservation-type": "reservation-test-not-match",
-					},
-				},
-				Spec: schedulingv1alpha1.ReservationSpec{
-					Owners: []schedulingv1alpha1.ReservationOwner{
-						{
-							LabelSelector: &metav1.LabelSelector{
-								MatchLabels: map[string]string{
-									"app": "test",
-								},
-							},
-						},
-					},
-				},
-			},
-			reservationAffinity: &apiext.ReservationAffinity{
-				RequiredDuringSchedulingIgnoredDuringExecution: &apiext.ReservationAffinitySelector{
-					ReservationSelectorTerms: []corev1.NodeSelectorTerm{
-						{
-							MatchExpressions: []corev1.NodeSelectorRequirement{
-								{
-									Key:      "reservation-type",
-									Operator: corev1.NodeSelectorOpIn,
-									Values:   []string{"reservation-test"},
-								},
-							},
-						},
-					},
-				},
-			},
-			want: false,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			pod := &corev1.Pod{
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "test-pod",
-				},
-			}
-			if tt.reservationAffinity != nil {
-				affinityData, err := json.Marshal(tt.reservationAffinity)
-				assert.NoError(t, err)
-				if pod.Annotations == nil {
-					pod.Annotations = map[string]string{}
-				}
-				pod.Annotations[apiext.AnnotationReservationAffinity] = string(affinityData)
-			}
-			reservationAffinity, err := reservationutil.GetRequiredReservationAffinity(pod)
-			assert.NoError(t, err)
-			rInfo := frameworkext.NewReservationInfo(tt.reservation)
-			got := matchReservationAffinity(&corev1.Node{}, rInfo, reservationAffinity)
-			assert.Equal(t, tt.want, got)
-		})
-	}
-}
-
 func TestBeforePreFilterWithReservationAffinity(t *testing.T) {
 	node := &corev1.Node{
 		ObjectMeta: metav1.ObjectMeta{