1#!/usr/bin/env python3
2#
3# Copyright (C) 2022 Intel Corporation.
4#
5# SPDX-License-Identifier: BSD-3-Clause
6#
7
8import sys, os
9sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library'))
10import acrn_config_utilities
11import re
12from collections import defaultdict
13from collections import namedtuple
14from acrn_config_utilities import get_node
15
16policy_owner = namedtuple("policy_owner", ["vm_name", "vcpu", "cache_type"])
17
18class Policy:
19    def __init__(self, cache_id, clos_mask):
20        self.cache_id = cache_id
21        self.clos_mask = clos_mask
22
23    def get_cache_id(self):
24        return self.cache_id
25
26    def get_clos_mask(self):
27        return self.clos_mask
28
29    def match_policy(self, src):
30        return (self.clos_mask == None) or (src.clos_mask == None) or (self.clos_mask == src.clos_mask)
31
32class L3Policy(Policy):
33    def __init__(self, policy):
34        self.cache_id = policy.get_cache_id()
35        self.clos_mask = policy.get_clos_mask()
36
37    def merge_policy(self, src):
38        return self.match_policy(src)
39
40class L2Policy:
41    cache2_id_list = []
42
43    def __init__(self, policy):
44        self.policy_list = []
45        for index,cache2_id in enumerate(self.cache2_id_list):
46            if cache2_id == policy.cache_id:
47                self.policy_list.append(policy)
48            else:
49                self.policy_list.append(Policy(None, None))
50
51    def get_cache_id(self, index):
52        return self.policy_list[index].get_cache_id()
53
54    def get_clos_mask(self, index):
55        return self.policy_list[index].get_clos_mask()
56
57    def match_policy(self, src):
58        for index in range(0, len(self.policy_list)):
59            if not self.policy_list[index].match_policy(src.policy_list[index]):
60                return False
61        return True
62
63    def merge_policy(self, src):
64        if self.match_policy(src):
65            for index in range(0, len(self.policy_list)):
66                if self.policy_list[index].clos_mask is None:
67                    self.policy_list[index].clos_mask = src.policy_list[index].clos_mask
68            return True
69        return False
70
71class RdtPolicy:
72    def __init__(self, policy_list, policy_owner):
73        cache2_id = None
74        cache3_id = None
75        l2_mask = None
76        l3_mask = None
77        for policy in policy_list:
78            cache_level = get_node("../CACHE_LEVEL/text()", policy)
79            cache_id = get_node("../CACHE_ID/text()", policy)
80            clos_mask = get_node("./CLOS_MASK/text()", policy)
81            if cache_level == "2":
82                l2_mask = clos_mask
83                cache2_id = cache_id
84            else:
85                l3_mask = clos_mask
86                cache3_id = cache_id
87        self.l2policy = L2Policy(Policy(cache2_id, l2_mask))
88        self.l3policy = L3Policy(Policy(cache3_id, l3_mask))
89
90        # a list stored the vCPU or VM info
91        self.policy_owner_list = [policy_owner]
92
93    def match_policy(self, src):
94        return self.l2policy.match_policy(src.l2policy) and self.l3policy.match_policy(src.l3policy)
95
96    #check whether the src could be merged, if yes, add the src owner to policy_owner_list list and return True
97    def merge_policy(self, src):
98        if self.match_policy(src):
99            self.l2policy.merge_policy(src.l2policy)
100            self.l3policy.merge_policy(src.l3policy)
101            self.policy_owner_list += src.policy_owner_list
102            return True
103        return False
104
105    #check whether a VM/vCPU could use this policy
106    def find_policy_owner(self, policy_owner):
107        return policy_owner in self.policy_owner_list
108
109class vCatPolicy(RdtPolicy):
110    def merge_policy(self, src):
111        return False
112
113class CdpPolicy():
114    def __init__(self,data_list, code_list, owner):
115        self.data_policy = RdtPolicy(data_list, policy_owner(owner.vm_name, owner.vcpu, "Data"))
116        self.code_policy = RdtPolicy(code_list, policy_owner(owner.vm_name, owner.vcpu, "Code"))
117
118    def merge_policy(self, src):
119        if self.code_policy.match_policy(src.code_policy) and self.data_policy.match_policy(src.data_policy):
120            self.code_policy.merge_policy(src.code_policy)
121            self.data_policy.merge_policy(src.data_policy)
122            return True
123        return False
124
125def merge_policy_list(policy_list):
126    result_list = []
127    for index,p in enumerate(policy_list):
128        merged = False
129        for result in result_list:
130            if result.merge_policy(p):
131                merged = True
132                break;
133        if not merged:
134            result_list.append(p)
135    return result_list
136
137def gen_policy_owner_list(scenario_etree):
138    policy_owner_list = []
139    vm_list = scenario_etree.xpath("//POLICY/VM")
140    for vm in vm_list:
141        vm_name = get_node("./text()", vm)
142        vcpu = get_node("../VCPU/text()", vm)
143        cache_type = get_node("../TYPE/text()", vm)
144        policy_owner_list.append(policy_owner(vm_name, int(vcpu), cache_type))
145    return policy_owner_list
146
147def vm_vcat_enable(scenario_etree, vm_name):
148    vcat_enable = get_node(f"//VCAT_ENABLED/text()", scenario_etree)
149    virtual_cat_support = get_node(f"//vm[name = '{vm_name}']/virtual_cat_support/text()", scenario_etree)
150    return (vcat_enable == "y") and (virtual_cat_support == "y")
151
152def cdp_enable(scenario_etree):
153    cdp_enable = get_node(f"//CDP_ENABLED/text()", scenario_etree)
154    return cdp_enable == "y"
155
156def convert_cdp_to_normal(cdp_policy_list):
157    policy_list = []
158    for cdp_policy in cdp_policy_list:
159        policy_list.append(cdp_policy.data_policy)
160        policy_list.append(cdp_policy.code_policy)
161    return policy_list
162
163def get_policy_list(scenario_etree):
164    init_cache2_id_list(scenario_etree)
165    policy_owner_list = gen_policy_owner_list(scenario_etree)
166
167    result_list = []
168    for policy_owner in policy_owner_list:
169        dict_tmp = {}
170        policy_list = scenario_etree.xpath(f"//POLICY[VM = '{policy_owner.vm_name}' and VCPU = '{policy_owner.vcpu}']")
171        if cdp_enable(scenario_etree):
172            data_list = scenario_etree.xpath(f"//POLICY[VM = '{policy_owner.vm_name}' and VCPU = '{policy_owner.vcpu}' and TYPE = 'Data']")
173            code_list = scenario_etree.xpath(f"//POLICY[VM = '{policy_owner.vm_name}' and VCPU = '{policy_owner.vcpu}' and TYPE = 'Code']")
174            if policy_owner.cache_type == "Code":
175                continue
176            elif policy_owner.cache_type == "Data":
177                result_list.append(CdpPolicy(data_list, code_list, policy_owner))
178        elif vm_vcat_enable(scenario_etree, policy_owner.vm_name):
179            result_list.append(vCatPolicy(policy_list, policy_owner))
180        else:
181            result_list.append(RdtPolicy(policy_list, policy_owner))
182    result_list = merge_policy_list(result_list)
183
184    if cdp_enable(scenario_etree):
185        result_list = convert_cdp_to_normal(result_list)
186
187    return result_list
188
189def init_cache2_id_list(scenario_etree):
190    cache2_id_list = scenario_etree.xpath("//CACHE_ALLOCATION[CACHE_LEVEL = 2]/CACHE_ID/text()")
191    cache2_id_list.sort()
192    L2Policy.cache2_id_list = cache2_id_list
193