VOL-1447: Changes in OpenOLT driver for creating the Traffic Schedulers and
Queues as per the TechProfile configuration

Change-Id: I3a51ce53c8f9bd369b89b5f1f55f74f73893d65e
diff --git a/agent/common/core.h b/agent/common/core.h
index 01e09a0..22ef8a9 100644
--- a/agent/common/core.h
+++ b/agent/common/core.h
@@ -42,7 +42,7 @@
 unsigned NumNniIf_();
 unsigned NumPonIf_();
 Status OmciMsgOut_(uint32_t intf_id, uint32_t onu_id, const std::string pkt);
-Status OnuPacketOut_(uint32_t intf_id, uint32_t onu_id, uint32_t port_no, const std::string pkt);
+Status OnuPacketOut_(uint32_t intf_id, uint32_t onu_id, uint32_t port_no, uint32_t gemport_id, const std::string pkt);
 Status ProbeDeviceCapabilities_();
 Status ProbePonIfTechnology_();
 Status UplinkPacketOut_(uint32_t intf_id, const std::string pkt);
@@ -55,8 +55,10 @@
 Status Disable_();
 Status Reenable_();
 Status GetDeviceInfo_(openolt::DeviceInfo* device_info);
-Status CreateTconts_(const openolt::Tconts *tconts);
-Status RemoveTconts_(const openolt::Tconts *tconts);
+Status CreateTrafficSchedulers_(const tech_profile::TrafficSchedulers *traffic_scheds);
+Status RemoveTrafficSchedulers_(const tech_profile::TrafficSchedulers *traffic_scheds);
+Status CreateTrafficQueues_(const tech_profile::TrafficQueues *traffic_queues);
+Status RemoveTrafficQueues_(const tech_profile::TrafficQueues *traffic_queues);
 uint32_t GetPortNum_(uint32_t flow_id);
 
 void stats_collection();
diff --git a/agent/common/server.cc b/agent/common/server.cc
index 03707cd..f87b85e 100644
--- a/agent/common/server.cc
+++ b/agent/common/server.cc
@@ -31,6 +31,7 @@
 
 #include <grpc++/grpc++.h>
 #include <openolt.grpc.pb.h>
+#include <tech_profile.grpc.pb.h>
 
 using grpc::Server;
 using grpc::ServerBuilder;
@@ -110,6 +111,7 @@
             request->intf_id(),
             request->onu_id(),
             request->port_no(),
+            request->gemport_id(),
             request->pkt());
     }
 
@@ -258,19 +260,35 @@
 
     }
 
-    Status CreateTconts(
+    Status CreateTrafficSchedulers(
             ServerContext* context,
-            const openolt::Tconts* request,
+            const tech_profile::TrafficSchedulers* request,
             openolt::Empty* response) override {
-        CreateTconts_(request);
+        CreateTrafficSchedulers_(request);
         return Status::OK;
     };
 
-    Status RemoveTconts(
+    Status RemoveTrafficSchedulers(
             ServerContext* context,
-            const openolt::Tconts* request,
+            const tech_profile::TrafficSchedulers* request,
             openolt::Empty* response) override {
-        RemoveTconts_(request);
+        RemoveTrafficSchedulers_(request);
+        return Status::OK;
+    };
+
+    Status CreateTrafficQueues(
+            ServerContext* context,
+            const tech_profile::TrafficQueues* request,
+            openolt::Empty* response) override {
+        CreateTrafficQueues_(request);
+        return Status::OK;
+    };
+
+    Status RemoveTrafficQueues(
+            ServerContext* context,
+            const tech_profile::TrafficQueues* request,
+            openolt::Empty* response) override {
+        RemoveTrafficQueues_(request);
         return Status::OK;
     };
 
diff --git a/agent/src/core.cc b/agent/src/core.cc
index c9781ff..2912932 100644
--- a/agent/src/core.cc
+++ b/agent/src/core.cc
@@ -24,6 +24,7 @@
 #include <sstream>
 #include <chrono>
 #include <thread>
+#include <bitset>
 
 #include "device.h"
 #include "core.h"
@@ -55,6 +56,9 @@
 
 #define MAX_SUPPORTED_INTF 16
 #define BAL_RSC_MANAGER_BASE_TM_SCHED_ID 16384
+#define MAX_TM_QUEUE_ID 8192
+#define MAX_TM_SCHED_ID 16384
+#define EAP_ETHER_TYPE 34958
 
 static unsigned int num_of_nni_ports = 0;
 static unsigned int num_of_pon_ports = 0;
@@ -67,7 +71,10 @@
 
 const uint32_t tm_upstream_sched_id_start = 18432;
 const uint32_t tm_downstream_sched_id_start = 16384;
-const uint32_t tm_queue_id_start = 4; //0 to 3 are default queues. Lets not use them.
+//0 to 3 are default queues. Lets not use them.
+const uint32_t tm_queue_id_start = 4;
+// Upto 8 fixed Upstream. Queue id 0 to 3 are pre-created, lets not use them.
+const uint32_t us_fixed_queue_id_list[8] = {4, 5, 6, 7, 8, 9, 10, 11};
 const std::string upstream = "upstream";
 const std::string downstream = "downstream";
 
@@ -76,18 +83,51 @@
 static std::map<uint32_t, uint32_t> flowid_to_port; // For mapping upstream flows to logical ports
 static std::map<uint32_t, uint32_t> flowid_to_gemport; // For mapping downstream flows into gemports
 static std::map<uint32_t, std::set<uint32_t> > port_to_flows; // For mapping logical ports to downstream flows
-static std::map<uint32_t, uint32_t> port_to_alloc;
-static bcmos_fastlock flow_lock;
+
+// This represents the Key to 'queue_map' map.
+// Represents (pon_intf_id, onu_id, uni_id, gemport_id, direction)
+typedef std::tuple<uint32_t, uint32_t, uint32_t, uint32_t, std::string> queue_map_key_tuple;
+// 'queue_map' maps queue_map_key_tuple to downstream queue id present
+// on the Subscriber Scheduler
+static std::map<queue_map_key_tuple, int> queue_map;
+// This represents the Key to 'sched_map' map.
+// Represents (pon_intf_id, onu_id, uni_id, direction)
+
+typedef std::tuple<uint32_t, uint32_t, uint32_t, std::string> sched_map_key_tuple;
+// 'sched_map' maps sched_map_key_tuple to DBA (Upstream) or
+// Subscriber (Downstream) Scheduler ID
+static std::map<sched_map_key_tuple, int> sched_map;
+
+
+std::bitset<MAX_TM_QUEUE_ID> tm_queue_bitset;
+std::bitset<MAX_TM_SCHED_ID> tm_sched_bitset;
+
+static bcmos_fastlock data_lock;
 
 #define MIN_ALLOC_ID_GPON 256
 #define MIN_ALLOC_ID_XGSPON 1024
 
-Status SchedAdd_(std::string direction, uint32_t access_intf_id, uint32_t onu_id, uint32_t uni_id, uint32_t port_no, 
-                 uint32_t alloc_id, openolt::AdditionalBW additional_bw, uint32_t weight, uint32_t priority,
-                 openolt::SchedulingPolicy sched_policy, openolt::TrafficShapingInfo traffic_shaping_info);
-static Status SchedRemove_(std::string direction, int intf_id, int onu_id, int uni_id, uint32_t port_no, int alloc_id);
+static bcmos_errno CreateSched(std::string direction, uint32_t access_intf_id, uint32_t onu_id, uint32_t uni_id, \
+                          uint32_t port_no, uint32_t alloc_id, tech_profile::AdditionalBW additional_bw, uint32_t weight, \
+                          uint32_t priority, tech_profile::SchedulingPolicy sched_policy,
+                          tech_profile::TrafficShapingInfo traffic_shaping_info);
+static bcmos_errno RemoveSched(int intf_id, int onu_id, int uni_id, std::string direction);
+static bcmos_errno CreateQueue(std::string direction, uint32_t access_intf_id, uint32_t onu_id, uint32_t uni_id, \
+                               uint32_t priority, uint32_t gemport_id);
+static bcmos_errno RemoveQueue(std::string direction, int intf_id, int onu_id, int uni_id, uint32_t port_no, int alloc_id);
 
-static inline int mk_sched_id(int intf_id, int onu_id, std::string direction) {
+/**
+* Returns the default NNI (Upstream direction) or PON (Downstream direction) scheduler
+* Every NNI port and PON port have default scheduler.
+* The NNI0 default scheduler ID is 18432, and NNI1 is 18433 and so on.
+* Similarly, PON0 default scheduler ID is 16384. PON1 is 16385 and so on.
+*
+* @param intf_id NNI or PON interface ID
+* @param direction "upstream" or "downstream"
+*
+* @return default scheduler ID for the given interface.
+*/
+static inline int get_default_tm_sched_id(int intf_id, std::string direction) {
     if (direction.compare(upstream) == 0) {
         return tm_upstream_sched_id_start + intf_id;
     } else if (direction.compare(downstream) == 0) {
@@ -99,17 +139,164 @@
     }
 }
 
-static inline int mk_queue_id(int pon_intf_id, int onu_id, int uni_id, uint32_t port_no, uint32_t alloc_id) {
-    (void) uni_id; // unused
-    if(port_no == 0) return tm_queue_id_start + pon_intf_id * 32 + onu_id; // old style
+/**
+* Gets a unique tm_queue_id for a given intf_id, onu_id, uni_id, gemport_id, direction
+* The tm_queue_id is locally cached in a map, so that it can rendered when necessary.
+* VOLTHA replays whole configuration on OLT reboot, so caching locally is not a problem
+*
+* @param intf_id NNI or PON intf ID
+* @param onu_id ONU ID
+* @param uni_id UNI ID
+* @param gemport_id GEM Port ID
+* @param direction Upstream or downstream
+*
+* @return tm_queue_id
+*/
+int get_tm_queue_id(int intf_id, int onu_id, int uni_id, int gemport_id, std::string direction) {
+    queue_map_key_tuple key(intf_id, onu_id, uni_id, gemport_id, direction);
+    int queue_id = -1;
 
-    int start = intf_technologies[pon_intf_id] == "gpon" ? MIN_ALLOC_ID_GPON : MIN_ALLOC_ID_XGSPON; // offset built into all alloc Ids
-    return tm_queue_id_start + alloc_id - start; // use alloc_id as a unique queue id. It is unique per UNI, and in the range of IDs supported by BAL
+    std::map<queue_map_key_tuple, int>::const_iterator it = queue_map.find(key);
+    if (it != queue_map.end()) {
+        queue_id = it->second;
+    }
+    if (queue_id != -1) {
+        return queue_id;
+    }
+
+    bcmos_fastlock_lock(&data_lock);
+    // Complexity of O(n). Is there better way that can avoid linear search?
+    for (queue_id = 0; queue_id < MAX_TM_QUEUE_ID; queue_id++) {
+        if (tm_queue_bitset[queue_id] == 0) {
+            tm_queue_bitset[queue_id] = 1;
+            break;
+        }
+    }
+    bcmos_fastlock_unlock(&data_lock, 0);
+
+    if (queue_id < MAX_TM_QUEUE_ID) {
+        bcmos_fastlock_lock(&data_lock);
+        queue_map[key] = queue_id;
+        bcmos_fastlock_unlock(&data_lock, 0);
+        return queue_id;
+    } else {
+        return -1;
+    }
 }
 
-static inline int mk_agg_port_id(int intf_id, int onu_id) {
-    if (board_technology == "gpon") return 511 + intf_id * 32 + onu_id;
-    return 1023 + intf_id * 32 + onu_id;
+/**
+* Update tm_queue_id for a given intf_id, onu_id, uni_id, gemport_id, direction
+*
+* @param intf_id NNI or PON intf ID
+* @param onu_id ONU ID
+* @param uni_id UNI ID
+* @param gemport_id GEM Port ID
+* @param direction Upstream or downstream
+* @param tm_queue_id tm_queue_id
+*/
+void update_tm_queue_id(int pon_intf_id, int onu_id, int uni_id, int gemport_id, std::string direction,
+                                  uint32_t queue_id) {
+    queue_map_key_tuple key(pon_intf_id, onu_id, uni_id, gemport_id, direction);
+    bcmos_fastlock_lock(&data_lock);
+    queue_map[key] = queue_id;
+    bcmos_fastlock_unlock(&data_lock, 0);
+}
+
+/**
+* Free tm_queue_id for a given intf_id, onu_id, uni_id, gemport_id, direction
+*
+* @param intf_id NNI or PON intf ID
+* @param onu_id ONU ID
+* @param uni_id UNI ID
+* @param gemport_id GEM Port ID
+* @param direction Upstream or downstream
+*/
+void free_tm_queue_id(int pon_intf_id, int onu_id, int uni_id, int gemport_id, std::string direction) {
+    queue_map_key_tuple key(pon_intf_id, onu_id, uni_id, gemport_id, direction);
+    std::map<queue_map_key_tuple, int>::const_iterator it;
+    bcmos_fastlock_lock(&data_lock);
+    it = queue_map.find(key);
+    if (it != queue_map.end()) {
+        tm_queue_bitset[it->second] = 0;
+        queue_map.erase(it);
+    }
+    bcmos_fastlock_unlock(&data_lock, 0);
+}
+
+/**
+* Gets a unique tm_sched_id for a given intf_id, onu_id, uni_id, gemport_id, direction
+* The tm_sched_id is locally cached in a map, so that it can rendered when necessary.
+* VOLTHA replays whole configuration on OLT reboot, so caching locally is not a problem
+*
+* @param intf_id NNI or PON intf ID
+* @param onu_id ONU ID
+* @param uni_id UNI ID
+* @param gemport_id GEM Port ID
+* @param direction Upstream or downstream
+*
+* @return tm_sched_id
+*/
+uint32_t get_tm_sched_id(int pon_intf_id, int onu_id, int uni_id, std::string direction) {
+    sched_map_key_tuple key(pon_intf_id, onu_id, uni_id, direction);
+    int sched_id = -1;
+
+    std::map<sched_map_key_tuple, int>::const_iterator it = sched_map.find(key);
+    if (it != sched_map.end()) {
+        sched_id = it->second;
+    }
+    if (sched_id != -1) {
+        return sched_id;
+    }
+
+    bcmos_fastlock_lock(&data_lock);
+    // Complexity of O(n). Is there better way that can avoid linear search?
+    for (sched_id = 0; sched_id < MAX_TM_SCHED_ID; sched_id++) {
+        if (tm_sched_bitset[sched_id] == 0) {
+            tm_sched_bitset[sched_id] = 1;
+            break;
+        }
+    }
+    bcmos_fastlock_unlock(&data_lock, 0);
+
+    if (sched_id < MAX_TM_SCHED_ID) {
+        bcmos_fastlock_lock(&data_lock);
+        sched_map[key] = sched_id;
+        bcmos_fastlock_unlock(&data_lock, 0);
+        return sched_id;
+    } else {
+        return -1;
+    }
+}
+
+/**
+* Free tm_sched_id for a given intf_id, onu_id, uni_id, gemport_id, direction
+*
+* @param intf_id NNI or PON intf ID
+* @param onu_id ONU ID
+* @param uni_id UNI ID
+* @param gemport_id GEM Port ID
+* @param direction Upstream or downstream
+*/
+void free_tm_sched_id(int pon_intf_id, int onu_id, int uni_id, std::string direction) {
+    sched_map_key_tuple key(pon_intf_id, onu_id, uni_id, direction);
+    std::map<sched_map_key_tuple, int>::const_iterator it;
+    bcmos_fastlock_lock(&data_lock);
+    it = sched_map.find(key);
+    if (it != sched_map.end()) {
+        tm_sched_bitset[it->second] = 0;
+        sched_map.erase(it);
+    }
+    bcmos_fastlock_unlock(&data_lock, 0);
+}
+
+bool is_tm_sched_id_present(int pon_intf_id, int onu_id, int uni_id, std::string direction) {
+    sched_map_key_tuple key(pon_intf_id, onu_id, uni_id, direction);
+    return sched_map.count(key) > 0 ? true: false;
+}
+
+bool is_tm_queue_id_present(int pon_intf_id, int onu_id, int uni_id, int gemport_id, std::string direction) {
+    queue_map_key_tuple key(pon_intf_id, onu_id, uni_id, gemport_id, direction);
+    return queue_map.count(key) > 0 ? true: false;
 }
 
 char* openolt_read_sysinfo(char* field_name, char* field_val)
@@ -260,7 +447,7 @@
 
         vendor_init();
         bcmbal_init(argc, argv, NULL);
-        bcmos_fastlock_init(&flow_lock, 0);
+        bcmos_fastlock_init(&data_lock, 0);
 
         BCM_LOG(INFO, openolt_log_id, "Enable OLT - %s-%s\n", VENDOR_ID, MODEL_ID);
 
@@ -309,7 +496,7 @@
     //This fails with Operation Not Supported, bug ???
 
     //TEMPORARY WORK AROUND
-    Status status = DisableUplinkIf_(0);
+    Status status = DisableUplinkIf_(nni_intf_id);
     if (status.ok()) {
         state.deactivate();
         openolt::Indication ind;
@@ -622,8 +809,8 @@
 
     if (0 == key.sub_term_id)
     {
-            BCM_LOG(INFO, openolt_log_id,"Invalid Key to handle subscriber terminal clear subscriber_terminal_id %d, Interface ID %d\n",
-                onu_id, intf_id);
+            BCM_LOG(INFO, openolt_log_id,"Invalid Key to handle subscriber terminal clear subscriber_terminal_id %d, \
+                    Interface ID %d\n", onu_id, intf_id);
             return Status(grpc::StatusCode::INTERNAL, "Failed to delete ONU");
     }
 
@@ -692,33 +879,35 @@
     return Status::OK;
 }
 
-Status OnuPacketOut_(uint32_t intf_id, uint32_t onu_id, uint32_t port_no, const std::string pkt) {
+Status OnuPacketOut_(uint32_t intf_id, uint32_t onu_id, uint32_t port_no, uint32_t gemport_id, const std::string pkt) {
     bcmos_errno err = BCM_ERR_OK;
     bcmbal_dest proxy_pkt_dest;
     bcmbal_u8_list_u32_max_2048 buf;
 
     if (port_no > 0) {
         bool found = false;
-        uint32_t gemport_id;
-
-        bcmos_fastlock_lock(&flow_lock);
-        // Map the port_no to one of the flows that owns it to find a gemport_id for that flow.
-        // Pick any flow that is mapped with the same port_no.
-        std::map<uint32_t, std::set<uint32_t> >::const_iterator it = port_to_flows.find(port_no);
-        if (it != port_to_flows.end() && !it->second.empty()) {
-            uint32_t flow_id = *(it->second.begin()); // Pick any flow_id out of the bag set
-            std::map<uint32_t, uint32_t>::const_iterator fit = flowid_to_gemport.find(flow_id);
-            if (fit != flowid_to_gemport.end()) {
-                found = true;
-                gemport_id = fit->second;
+        if (gemport_id == 0) {
+            bcmos_fastlock_lock(&data_lock);
+            // Map the port_no to one of the flows that owns it to find a gemport_id for that flow.
+            // Pick any flow that is mapped with the same port_no.
+            std::map<uint32_t, std::set<uint32_t> >::const_iterator it = port_to_flows.find(port_no);
+            if (it != port_to_flows.end() && !it->second.empty()) {
+                uint32_t flow_id = *(it->second.begin()); // Pick any flow_id out of the bag set
+                std::map<uint32_t, uint32_t>::const_iterator fit = flowid_to_gemport.find(flow_id);
+                if (fit != flowid_to_gemport.end()) {
+                    found = true;
+                    gemport_id = fit->second;
+                }
             }
-        }
-        bcmos_fastlock_unlock(&flow_lock, 0);
+            bcmos_fastlock_unlock(&data_lock, 0);
 
-        if (!found) {
-            BCM_LOG(ERROR, openolt_log_id, "Packet out failed to find destination for ONU %d port_no %u on PON %d\n",
-                onu_id, port_no, intf_id);
-            return grpc::Status(grpc::StatusCode::NOT_FOUND, "no flow for port_no");
+            if (!found) {
+                BCM_LOG(ERROR, openolt_log_id, "Packet out failed to find destination for ONU %d port_no %u on PON %d\n",
+                        onu_id, port_no, intf_id);
+                return grpc::Status(grpc::StatusCode::NOT_FOUND, "no flow for port_no");
+            }
+            BCM_LOG(INFO, openolt_log_id, "Gem port %u found for ONU %d port_no %u on PON %d\n",
+                    gemport_id, onu_id, port_no, intf_id);
         }
 
         proxy_pkt_dest.type = BCMBAL_DEST_TYPE_SVC_PORT;
@@ -770,13 +959,13 @@
 
 uint32_t GetPortNum_(uint32_t flow_id)
 {
-    bcmos_fastlock_lock(&flow_lock);
+    bcmos_fastlock_lock(&data_lock);
     uint32_t port_no = 0;
     std::map<uint32_t, uint32_t >::const_iterator it = flowid_to_port.find(flow_id);
     if (it != flowid_to_port.end()) {
         port_no = it->second;
     }
-    bcmos_fastlock_unlock(&flow_lock, 0);
+    bcmos_fastlock_unlock(&data_lock, 0);
     return port_no;
 }
 
@@ -788,14 +977,18 @@
     bcmos_errno err;
     bcmbal_flow_cfg cfg;
     bcmbal_flow_key key = { };
+    int32_t o_vid = -1;
+    bool single_tag = false;
+    uint32_t ether_type = 0;
 
-    BCM_LOG(INFO, openolt_log_id, "flow add - intf_id %d, onu_id %d, uni_id %d, port_no %u, flow_id %d, flow_type %s, gemport_id %d, network_intf_id %d, cookie %llu\n",
-        access_intf_id, onu_id, uni_id, port_no, flow_id, flow_type.c_str(), gemport_id, network_intf_id, cookie);
+    BCM_LOG(INFO, openolt_log_id, "flow add - intf_id %d, onu_id %d, uni_id %d, port_no %u, flow_id %d, flow_type %s, \
+            gemport_id %d, network_intf_id %d, cookie %llu\n", \
+            access_intf_id, onu_id, uni_id, port_no, flow_id, flow_type.c_str(), gemport_id, network_intf_id, cookie);
 
     key.flow_id = flow_id;
-    if (flow_type.compare("upstream") == 0 ) {
+    if (flow_type.compare(upstream) == 0 ) {
         key.flow_type = BCMBAL_FLOW_TYPE_UPSTREAM;
-    } else if (flow_type.compare("downstream") == 0) {
+    } else if (flow_type.compare(downstream) == 0) {
         key.flow_type = BCMBAL_FLOW_TYPE_DOWNSTREAM;
     } else {
         BCM_LOG(WARNING, openolt_log_id, "Invalid flow type %s\n", flow_type.c_str());
@@ -820,7 +1013,7 @@
         BCMBAL_CFG_PROP_SET(&cfg, flow, svc_port_id, gemport_id);
     }
     if (gemport_id >= 0 && port_no != 0) {
-        bcmos_fastlock_lock(&flow_lock);
+        bcmos_fastlock_lock(&data_lock);
         if (key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM) {
             port_to_flows[port_no].insert(key.flow_id);
             flowid_to_gemport[key.flow_id] = gemport_id;
@@ -829,7 +1022,7 @@
         {
             flowid_to_port[key.flow_id] = port_no;
         }
-        bcmos_fastlock_unlock(&flow_lock, 0);
+        bcmos_fastlock_unlock(&data_lock, 0);
     }
     if (priority_value >= 0) {
         BCMBAL_CFG_PROP_SET(&cfg, flow, priority, priority_value);
@@ -858,17 +1051,8 @@
             BCMBAL_ATTRIBUTE_PROP_SET(&val, classifier, i_vid, classifier.i_vid());
         }
 
-        if (classifier.o_pbits()) {
-            BCM_LOG(DEBUG, openolt_log_id, "classify o_pbits 0x%x\n", classifier.o_pbits());
-            BCMBAL_ATTRIBUTE_PROP_SET(&val, classifier, o_pbits, classifier.o_pbits());
-        }
-
-        if (classifier.i_pbits()) {
-            BCM_LOG(DEBUG, openolt_log_id, "classify i_pbits 0x%x\n", classifier.i_pbits());
-            BCMBAL_ATTRIBUTE_PROP_SET(&val, classifier, i_pbits, classifier.i_pbits());
-        }
-
         if (classifier.eth_type()) {
+            ether_type = classifier.eth_type();
             BCM_LOG(DEBUG, openolt_log_id, "classify ether_type 0x%04x\n", classifier.eth_type());
             BCMBAL_ATTRIBUTE_PROP_SET(&val, classifier, ether_type, classifier.eth_type());
         }
@@ -914,8 +1098,15 @@
                 BCMBAL_ATTRIBUTE_PROP_SET(&val, classifier, pkt_tag_type, BCMBAL_PKT_TAG_TYPE_UNTAGGED);
             } else if (classifier.pkt_tag_type().compare("single_tag") == 0) {
                 BCMBAL_ATTRIBUTE_PROP_SET(&val, classifier, pkt_tag_type, BCMBAL_PKT_TAG_TYPE_SINGLE_TAG);
+                single_tag = true;
+
+		BCM_LOG(DEBUG, openolt_log_id, "classify o_pbits 0x%x\n", classifier.o_pbits());
+                BCMBAL_ATTRIBUTE_PROP_SET(&val, classifier, o_pbits, classifier.o_pbits());
             } else if (classifier.pkt_tag_type().compare("double_tag") == 0) {
                 BCMBAL_ATTRIBUTE_PROP_SET(&val, classifier, pkt_tag_type, BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG);
+
+		BCM_LOG(DEBUG, openolt_log_id, "classify o_pbits 0x%x\n", classifier.o_pbits());
+                BCMBAL_ATTRIBUTE_PROP_SET(&val, classifier, o_pbits, classifier.o_pbits());
             }
         }
 
@@ -944,6 +1135,7 @@
 
         if (action.o_vid()) {
             BCM_LOG(INFO, openolt_log_id, "action o_vid=%d\n", action.o_vid());
+            o_vid = action.o_vid();
             BCMBAL_ATTRIBUTE_PROP_SET(&val, action, o_vid, action.o_vid());
         }
 
@@ -979,24 +1171,23 @@
 
         if (key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM) {
             bcmbal_tm_queue_ref val = { };
-            val.sched_id = mk_sched_id(access_intf_id, onu_id, "downstream");
-            uint32_t alloc_id;
-            bcmos_fastlock_lock(&flow_lock);
-            alloc_id = port_to_alloc[port_no];
-            bcmos_fastlock_unlock(&flow_lock, 0);
-            val.queue_id = mk_queue_id(access_intf_id, onu_id, uni_id, port_no, alloc_id);
+            if (single_tag && ether_type == EAP_ETHER_TYPE) {
+                val.sched_id = get_default_tm_sched_id(access_intf_id, downstream);
+                val.queue_id = 0;
+
+            } else {
+                val.sched_id = get_tm_sched_id(access_intf_id, onu_id, uni_id, downstream); // Subscriber Scheduler
+                val.queue_id = get_tm_queue_id(access_intf_id, onu_id, uni_id, gemport_id, downstream);
+            }
             BCMBAL_CFG_PROP_SET(&cfg, flow, queue, val);
         } else if (key.flow_type == BCMBAL_FLOW_TYPE_UPSTREAM) {
             bcmbal_tm_sched_id val1;
-            if (alloc_id != 0) {
-                val1 = alloc_id;
-            } else {
-                BCM_LOG(ERROR, openolt_log_id, "alloc_id not present");
-            }
+            val1 = get_tm_sched_id(access_intf_id, onu_id, uni_id, upstream); // DBA Scheduler ID
             BCMBAL_CFG_PROP_SET(&cfg, flow, dba_tm_sched_id, val1);
 
             bcmbal_tm_queue_ref val2 = { };
-            val2.sched_id = mk_sched_id(network_intf_id, onu_id, "upstream");
+            val2.sched_id = get_default_tm_sched_id(network_intf_id, upstream); // NNI Scheduler ID
+            val2.queue_id = get_tm_queue_id(network_intf_id, onu_id, uni_id, gemport_id, upstream); // Queue on NNI
             BCMBAL_CFG_PROP_SET(&cfg, flow, queue, val2);
         }
     }
@@ -1019,16 +1210,16 @@
 
     key.flow_id = (bcmbal_flow_id) flow_id;
     key.flow_id = flow_id;
-    if (flow_type.compare("upstream") == 0 ) {
+    if (flow_type.compare(upstream) == 0 ) {
         key.flow_type = BCMBAL_FLOW_TYPE_UPSTREAM;
-    } else if (flow_type.compare("downstream") == 0) {
+    } else if (flow_type.compare(downstream) == 0) {
         key.flow_type = BCMBAL_FLOW_TYPE_DOWNSTREAM;
     } else {
         BCM_LOG(WARNING, openolt_log_id, "Invalid flow type %s\n", flow_type.c_str());
         return bcm_to_grpc_err(BCM_ERR_PARM, "Invalid flow type");
     }
 
-    bcmos_fastlock_lock(&flow_lock);
+    bcmos_fastlock_lock(&data_lock);
     uint32_t port_no = flowid_to_port[key.flow_id];
     if (key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM) {
         flowid_to_gemport.erase(key.flow_id);
@@ -1039,7 +1230,7 @@
     {
         flowid_to_port.erase(key.flow_id);
     }
-    bcmos_fastlock_unlock(&flow_lock, 0);
+    bcmos_fastlock_unlock(&data_lock, 0);
 
     BCMBAL_CFG_INIT(&cfg, flow, key);
 
@@ -1055,217 +1246,375 @@
     return Status::OK;
 }
 
-Status SchedAdd_(std::string direction, uint32_t intf_id, uint32_t onu_id, uint32_t uni_id, uint32_t port_no,
-                 uint32_t alloc_id, openolt::AdditionalBW additional_bw, uint32_t weight, uint32_t priority,
-                 openolt::SchedulingPolicy sched_policy, openolt::TrafficShapingInfo tf_sh_info) {
+bcmos_errno CreateSched(std::string direction, uint32_t intf_id, uint32_t onu_id, uint32_t uni_id, uint32_t port_no,
+                 uint32_t alloc_id, tech_profile::AdditionalBW additional_bw, uint32_t weight, uint32_t priority,
+                 tech_profile::SchedulingPolicy sched_policy, tech_profile::TrafficShapingInfo tf_sh_info) {
 
     bcmos_errno err;
 
-    if (direction == "downstream") {
-        bcmbal_tm_queue_cfg cfg;
-        bcmbal_tm_queue_key key = { };
-        // Note: We use the default scheduler available in the DL.
-        key.sched_id = mk_sched_id(intf_id, onu_id, direction);
-        key.sched_dir = BCMBAL_TM_SCHED_DIR_DS;
-        key.id = mk_queue_id(intf_id, onu_id, uni_id, port_no, alloc_id);
+    if (direction == downstream) {
 
-        BCMBAL_CFG_INIT(&cfg, tm_queue, key);
-        //Queue must be set with either weight or priority, not both,
-        // as its scheduler' sched_type is sp_wfq
-        BCMBAL_CFG_PROP_SET(&cfg, tm_queue, priority, priority);
-        //BCMBAL_CFG_PROP_SET(&cfg, tm_queue, weight, weight);
-        //BCMBAL_CFG_PROP_SET(&cfg, tm_queue, creation_mode, BCMBAL_TM_CREATION_MODE_MANUAL);
-
-        bcmbal_tm_shaping rate = {};
-        if (tf_sh_info.cir() >= 0 && tf_sh_info.pir() > 0) {
-            uint32_t cir = tf_sh_info.cir();
-            uint32_t pir = tf_sh_info.pir();
-            uint32_t burst = tf_sh_info.pbs();
-            BCM_LOG(INFO, openolt_log_id, "applying traffic shaping in DL cir=%u, pir=%u, burst=%u\n",
-               cir, pir, burst);
-            rate.presence_mask = BCMBAL_TM_SHAPING_ID_ALL;
-            rate.cir = cir;
-            rate.pir = pir;
-            rate.burst = burst;
-
-            BCMBAL_CFG_PROP_SET(&cfg, tm_queue, rate, rate);
-        }
-
-        err = bcmbal_cfg_set(DEFAULT_ATERM_ID, &cfg.hdr);
-        if (err) {
-            BCM_LOG(ERROR, openolt_log_id, "Failed to create subscriber downstream tm queue, id %d, sched_id %d, intf_id %d, onu_id %d, uni_id %d, port_no %u, alt_id %d\n",
-                    key.id, key.sched_id, intf_id, onu_id, uni_id, port_no, alloc_id);
-            return bcm_to_grpc_err(err, "Failed to create subscriber downstream tm queue");
-        }
-
-        bcmos_fastlock_lock(&flow_lock);
-        port_to_alloc[port_no] = alloc_id;
-        bcmos_fastlock_unlock(&flow_lock, 0);
-
-        BCM_LOG(INFO, openolt_log_id, "Create downstream sched, id %d, intf_id %d, onu_id %d, uni_id %d, port_no %u, alt_id %d\n",
-                key.id,intf_id,onu_id,uni_id,port_no,alloc_id);
-
-    } else { //"upstream"
         bcmbal_tm_sched_cfg cfg;
         bcmbal_tm_sched_key key = { };
-        bcmbal_tm_sched_type sched_type;
+        key.id = get_tm_sched_id(intf_id, onu_id, uni_id, direction);
+        key.dir = BCMBAL_TM_SCHED_DIR_DS;
 
-        key.id = alloc_id;
+        BCMBAL_CFG_INIT(&cfg, tm_sched, key);
+
+        {
+            // bcmbal_tm_sched_owner
+            // In downstream it is sub_term scheduler
+            bcmbal_tm_sched_owner tm_sched_owner = { };
+            tm_sched_owner.type = BCMBAL_TM_SCHED_OWNER_TYPE_SUB_TERM;
+            tm_sched_owner.u.sub_term.intf_id = intf_id;
+            tm_sched_owner.u.sub_term.sub_term_id = onu_id;
+            BCMBAL_CFG_PROP_SET(&cfg, tm_sched, owner, tm_sched_owner);
+
+            // bcmbal_tm_sched_type
+            // set the deafult policy to strict priority
+            BCMBAL_CFG_PROP_SET(&cfg, tm_sched, sched_type, BCMBAL_TM_SCHED_TYPE_SP);
+
+            // bcmbal_tm_sched_parent
+            // The parent for the sub_term scheduler is the PON scheduler in the downstream
+            bcmbal_tm_sched_parent tm_sched_parent = { };
+            tm_sched_parent.presence_mask |= (BCMBAL_TM_SCHED_PARENT_ID_SCHED_ID);
+            tm_sched_parent.sched_id = get_default_tm_sched_id(intf_id, downstream);
+            tm_sched_parent.presence_mask |= (BCMBAL_TM_SCHED_PARENT_ID_PRIORITY);
+            tm_sched_parent.priority = 1; // TODO: Hardcoded priority as 1
+            BCMBAL_CFG_PROP_SET(&cfg, tm_sched, sched_parent, tm_sched_parent);
+
+            // num_priorities: Max number of strict priority scheduling elements
+            BCMBAL_CFG_PROP_SET(&cfg, tm_sched, num_priorities, 8); // TODO: hardcoded 8 priorities.
+
+            // bcmbal_tm_shaping
+            if (tf_sh_info.cir() >= 0 && tf_sh_info.pir() > 0) {
+                bcmbal_tm_shaping rate = {};
+                uint32_t cir = tf_sh_info.cir();
+                uint32_t pir = tf_sh_info.pir();
+                uint32_t burst = tf_sh_info.pbs();
+                BCM_LOG(INFO, openolt_log_id, "applying traffic shaping in DL cir=%u, pir=%u, burst=%u\n",
+                   cir, pir, burst);
+                rate.presence_mask = BCMBAL_TM_SHAPING_ID_NONE;
+                rate.presence_mask |= BCMBAL_TM_SHAPING_ID_PIR;
+                rate.presence_mask |= BCMBAL_TM_SHAPING_ID_BURST;
+                // FIXME: Setting CIR, results in BAL throwing error 'tm_sched minimum rate is not supported yet'
+                // rate.cir = cir;
+                rate.pir = pir;
+                rate.burst = burst;
+
+                BCMBAL_CFG_PROP_SET(&cfg, tm_queue, rate, rate);
+            }
+
+            // creation_mode
+            // BCMBAL_CFG_PROP_SET(&cfg, tm_sched, creation_mode, BCMBAL_TM_CREATION_MODE_MANUAL);
+        }
+
+        err = bcmbal_cfg_set(DEFAULT_ATERM_ID, &(cfg.hdr));
+        if (err) {
+            BCM_LOG(ERROR, openolt_log_id, "Failed to create downstream subscriber scheduler, id %d, intf_id %d, \
+                    onu_id %d, uni_id %d, port_no %u\n", key.id, intf_id, onu_id,uni_id,port_no);
+            return err;
+        }
+        BCM_LOG(INFO, openolt_log_id, "Create downstream subscriber sched, id %d, intf_id %d, onu_id %d, \
+                uni_id %d, port_no %u\n", key.id,intf_id,onu_id,uni_id,port_no);
+
+    } else { //upstream
+        bcmbal_tm_sched_cfg cfg;
+        bcmbal_tm_sched_key key = { };
+
+        key.id = get_tm_sched_id(intf_id, onu_id, uni_id, direction);
         key.dir = BCMBAL_TM_SCHED_DIR_US;
 
         BCMBAL_CFG_INIT(&cfg, tm_sched, key);
 
         {
-            bcmbal_tm_sched_owner val = { };
+            // bcmbal_tm_sched_owner: AGG PORT
+            bcmbal_tm_sched_owner tm_sched_owner = { };
+            tm_sched_owner.type = BCMBAL_TM_SCHED_OWNER_TYPE_AGG_PORT;
+            tm_sched_owner.u.agg_port.presence_mask |= bcmbal_tm_sched_owner_agg_port_id_all;
+            tm_sched_owner.u.agg_port.intf_id = intf_id;
+            tm_sched_owner.u.agg_port.sub_term_id = onu_id;
+            tm_sched_owner.u.agg_port.agg_port_id = alloc_id;
+            BCMBAL_CFG_PROP_SET(&cfg, tm_sched, owner, tm_sched_owner);
 
-            val.type = BCMBAL_TM_SCHED_OWNER_TYPE_AGG_PORT;
-            BCMBAL_ATTRIBUTE_PROP_SET(&val.u.agg_port, tm_sched_owner_agg_port, intf_id, (bcmbal_intf_id) intf_id);
-            BCMBAL_ATTRIBUTE_PROP_SET(&val.u.agg_port, tm_sched_owner_agg_port, sub_term_id, (bcmbal_sub_id) onu_id);
-            BCMBAL_ATTRIBUTE_PROP_SET(&val.u.agg_port, tm_sched_owner_agg_port, agg_port_id, (bcmbal_aggregation_port_id) alloc_id);
+            // bcmbal_tm_shaping
+            if (tf_sh_info.cir() >= 0 && tf_sh_info.pir() > 0) {
+                bcmbal_tm_shaping rate = {};
+                uint32_t cir = tf_sh_info.cir();
+                uint32_t pir = tf_sh_info.pir();
+                uint32_t burst = tf_sh_info.pbs();
+                BCM_LOG(INFO, openolt_log_id, "applying traffic shaping in UL cir=%u, pir=%u, burst=%u\n",
+                   cir, pir, burst);
+                rate.presence_mask = BCMBAL_TM_SHAPING_ID_ALL;
+                rate.cir = cir;
+                rate.pir = pir;
+                rate.burst = burst;
 
-            BCMBAL_CFG_PROP_SET(&cfg, tm_sched, owner, val);
-
-        }
-        bcmbal_tm_shaping rate = {};
-        if (tf_sh_info.cir() >= 0 && tf_sh_info.pir() > 0) {
-            uint32_t cir = tf_sh_info.cir();
-            uint32_t pir = tf_sh_info.pir();
-            uint32_t burst = tf_sh_info.pbs();
-            BCM_LOG(INFO, openolt_log_id, "applying traffic shaping in UL cir=%u, pir=%u, burst=%u\n",
-               cir, pir, burst);
-            rate.presence_mask = BCMBAL_TM_SHAPING_ID_ALL;
-            rate.cir = cir;
-            rate.pir = pir;
-            rate.burst = burst;
-
-            BCMBAL_CFG_PROP_SET(&cfg, tm_sched, rate, rate);
+                BCMBAL_CFG_PROP_SET(&cfg, tm_sched, rate, rate);
+            }
         }
 
         err = bcmbal_cfg_set(DEFAULT_ATERM_ID, &(cfg.hdr));
         if (err) {
-            BCM_LOG(ERROR, openolt_log_id, "Failed to create upstream DBA sched, id %d, intf_id %d, onu_id %d, uni_id %d, port_no %u, alloc_id %d\n",
-                    key.id, intf_id, onu_id,uni_id,port_no,alloc_id);
-            return bcm_to_grpc_err(err, "Failed to create upstream DBA sched");
+            BCM_LOG(ERROR, openolt_log_id, "Failed to create upstream DBA sched, id %d, intf_id %d, onu_id %d, uni_id %d,\
+                    port_no %u, alloc_id %d\n", key.id, intf_id, onu_id,uni_id,port_no,alloc_id);
+            return err;
         }
-        BCM_LOG(INFO, openolt_log_id, "Create upstream DBA sched, id %d, intf_id %d, onu_id %d, uni_id %d, port_no %u, alloc_id %d\n",
-                key.id,intf_id,onu_id,uni_id,port_no,alloc_id);
+        BCM_LOG(INFO, openolt_log_id, "Create upstream DBA sched, id %d, intf_id %d, onu_id %d, uni_id %d, port_no %u, \
+                alloc_id %d\n", key.id,intf_id,onu_id,uni_id,port_no,alloc_id);
     }
 
-    return Status::OK;
-
+    return BCM_ERR_OK;
 }
 
-Status CreateTconts_(const openolt::Tconts *tconts) {
-    uint32_t intf_id = tconts->intf_id();
-    uint32_t onu_id = tconts->onu_id();
-    uint32_t uni_id = tconts->uni_id();
-    uint32_t port_no = tconts->port_no();
+Status CreateTrafficSchedulers_(const tech_profile::TrafficSchedulers *traffic_scheds) {
+    uint32_t intf_id = traffic_scheds->intf_id();
+    uint32_t onu_id = traffic_scheds->onu_id();
+    uint32_t uni_id = traffic_scheds->uni_id();
+    uint32_t port_no = traffic_scheds->port_no();
     std::string direction;
     unsigned int alloc_id;
-    openolt::Scheduler scheduler;
-    openolt::AdditionalBW additional_bw;
+    tech_profile::SchedulerConfig sched_config;
+    tech_profile::AdditionalBW additional_bw;
     uint32_t priority;
     uint32_t weight;
-    openolt::SchedulingPolicy sched_policy;
-    openolt::TrafficShapingInfo traffic_shaping_info;
+    tech_profile::SchedulingPolicy sched_policy;
+    tech_profile::TrafficShapingInfo traffic_shaping_info;
+    bcmos_errno err;
 
-    for (int i = 0; i < tconts->tconts_size(); i++) {
-        openolt::Tcont tcont = tconts->tconts(i);
-        if (tcont.direction() == openolt::Direction::UPSTREAM) {
-            direction = "upstream";
-        } else if (tcont.direction() == openolt::Direction::DOWNSTREAM) {
-            direction = "downstream";
+    for (int i = 0; i < traffic_scheds->traffic_scheds_size(); i++) {
+        tech_profile::TrafficScheduler traffic_sched = traffic_scheds->traffic_scheds(i);
+        if (traffic_sched.direction() == tech_profile::Direction::UPSTREAM) {
+            direction = upstream;
+        } else if (traffic_sched.direction() == tech_profile::Direction::DOWNSTREAM) {
+            direction = downstream;
         }
         else {
-            BCM_LOG(ERROR, openolt_log_id, "direction-not-supported %d", tcont.direction());
+            BCM_LOG(ERROR, openolt_log_id, "direction-not-supported %d", traffic_sched.direction());
             return Status::CANCELLED;
         }
-        alloc_id = tcont.alloc_id();
-        scheduler = tcont.scheduler();
-        additional_bw = scheduler.additional_bw();
-        priority = scheduler.priority();
-        weight = scheduler.weight();
-        sched_policy = scheduler.sched_policy();
-        traffic_shaping_info = tcont.traffic_shaping_info();
-        SchedAdd_(direction, intf_id, onu_id, uni_id, port_no, alloc_id, additional_bw, weight, priority, sched_policy, traffic_shaping_info);
+        alloc_id = traffic_sched.alloc_id();
+        sched_config = traffic_sched.scheduler();
+        additional_bw = sched_config.additional_bw();
+        priority = sched_config.priority();
+        weight = sched_config.weight();
+        sched_policy = sched_config.sched_policy();
+        traffic_shaping_info = traffic_sched.traffic_shaping_info();
+        err =  CreateSched(direction, intf_id, onu_id, uni_id, port_no, alloc_id, additional_bw, weight, priority,
+                           sched_policy, traffic_shaping_info);
+        if (err) {
+            return bcm_to_grpc_err(err, "Failed to create scheduler");
+        }
     }
     return Status::OK;
 }
 
-Status RemoveTconts_(const openolt::Tconts *tconts) {
-    uint32_t intf_id = tconts->intf_id();
-    uint32_t onu_id = tconts->onu_id();
-    uint32_t uni_id = tconts->uni_id();
-    uint32_t port_no = tconts->port_no();
-    std::string direction;
-    unsigned int alloc_id;
-
-    for (int i = 0; i < tconts->tconts_size(); i++) {
-        openolt::Tcont tcont = tconts->tconts(i);
-        if (tcont.direction() == openolt::Direction::UPSTREAM) {
-            direction = "upstream";
-        } else if (tcont.direction() == openolt::Direction::DOWNSTREAM) {
-            direction = "downstream";
-        }
-        else {
-            BCM_LOG(ERROR, openolt_log_id, "direction-not-supported %d", tcont.direction());
-            return Status::CANCELLED;
-        }
-        alloc_id = tcont.alloc_id();
-        SchedRemove_(direction, intf_id, onu_id, uni_id, port_no, alloc_id);
-    }
-    return Status::OK;
-}
-
-Status SchedRemove_(std::string direction, int intf_id, int onu_id, int uni_id, uint32_t port_no, int alloc_id) {
+bcmos_errno RemoveSched(int intf_id, int onu_id, int uni_id, std::string direction) {
 
     bcmos_errno err;
 
+    bcmbal_tm_sched_cfg tm_cfg_us;
+    bcmbal_tm_sched_key tm_key_us = { };
 
-    if (direction == "upstream") {
-        // DBA sched
-        bcmbal_tm_sched_cfg tm_cfg_us;
-        bcmbal_tm_sched_key tm_key_us = { };
-
-        tm_key_us.id = alloc_id;
+    if (is_tm_sched_id_present(intf_id, onu_id, uni_id, direction)) {
+        tm_key_us.id = get_tm_sched_id(intf_id, onu_id, uni_id, direction);
+    } else {
+        BCM_LOG(INFO, openolt_log_id, "schduler not present in %s\n", direction.c_str());
+        return BCM_ERR_OK;
+    }
+    if (direction == upstream) {
         tm_key_us.dir = BCMBAL_TM_SCHED_DIR_US;
+    } else {
+        tm_key_us.dir = BCMBAL_TM_SCHED_DIR_DS;
+    }
 
-        BCMBAL_CFG_INIT(&tm_cfg_us, tm_sched, tm_key_us);
+    BCMBAL_CFG_INIT(&tm_cfg_us, tm_sched, tm_key_us);
 
-        err = bcmbal_cfg_clear(DEFAULT_ATERM_ID, &(tm_cfg_us.hdr));
-        if (err) {
-            BCM_LOG(ERROR, openolt_log_id, "Failed to remove upstream DBA sched, id %d, intf_id %d, onu_id %d\n",
-                tm_key_us.id, intf_id, onu_id);
-            return Status(grpc::StatusCode::INTERNAL, "Failed to remove upstream DBA sched");
+    err = bcmbal_cfg_clear(DEFAULT_ATERM_ID, &(tm_cfg_us.hdr));
+    if (err) {
+        BCM_LOG(ERROR, openolt_log_id, "Failed to remove scheduler sched, direction = %s, id %d, intf_id %d, onu_id %d\n", \
+                direction.c_str(), tm_key_us.id, intf_id, onu_id);
+        return err;
+    }
+
+    free_tm_sched_id(intf_id, onu_id, uni_id, direction);
+
+    BCM_LOG(INFO, openolt_log_id, "Removed sched, direction = %s, id %d, intf_id %d, onu_id %d\n", \
+            direction.c_str(), tm_key_us.id, intf_id, onu_id);
+
+    return BCM_ERR_OK;
+}
+
+Status RemoveTrafficSchedulers_(const tech_profile::TrafficSchedulers *traffic_scheds) {
+    uint32_t intf_id = traffic_scheds->intf_id();
+    uint32_t onu_id = traffic_scheds->onu_id();
+    uint32_t uni_id = traffic_scheds->uni_id();
+    std::string direction;
+    bcmos_errno err;
+
+    for (int i = 0; i < traffic_scheds->traffic_scheds_size(); i++) {
+        tech_profile::TrafficScheduler traffic_sched = traffic_scheds->traffic_scheds(i);
+        if (traffic_sched.direction() == tech_profile::Direction::UPSTREAM) {
+            direction = upstream;
+        } else if (traffic_sched.direction() == tech_profile::Direction::DOWNSTREAM) {
+            direction = downstream;
         }
+        else {
+            BCM_LOG(ERROR, openolt_log_id, "direction-not-supported %d", traffic_sched.direction());
+            return Status::CANCELLED;
+        }
+        err = RemoveSched(intf_id, onu_id, uni_id, direction);
+        if (err) {
+            return bcm_to_grpc_err(err, "error-removing-traffic-scheduler");
+        }
+    }
+    return Status::OK;
+}
 
-        BCM_LOG(INFO, openolt_log_id, "Remove upstream DBA sched, id %d, intf_id %d, onu_id %d\n",
-            tm_key_us.id, intf_id, onu_id);
+bcmos_errno CreateQueue(std::string direction, uint32_t access_intf_id, uint32_t onu_id, uint32_t uni_id, uint32_t priority,
+                        uint32_t gemport_id) {
+    bcmos_errno err;
+    bcmbal_tm_queue_cfg cfg;
+    bcmbal_tm_queue_key key = { };
+    if (direction == downstream) {
+        // In the downstream, the queues are on the 'sub term' scheduler
+        // There is one queue per gem port
+        key.sched_dir = BCMBAL_TM_SCHED_DIR_DS;
+        key.sched_id = get_tm_sched_id(access_intf_id, onu_id, uni_id, direction);
+        key.id = get_tm_queue_id(access_intf_id, onu_id, uni_id, gemport_id, direction);
 
-    } else if (direction == "downstream") {
-	    // Queue
+    } else {
+        queue_map_key_tuple map_key(access_intf_id, onu_id, uni_id, gemport_id, direction);
+        if (queue_map.count(map_key) > 0) {
+            BCM_LOG(INFO, openolt_log_id, "upstream queue exists for intf_id %d, onu_id %d, uni_id %d\n. Not re-creating", \
+                    access_intf_id, onu_id, uni_id); 
+            return BCM_ERR_OK;
+        }
+        key.sched_dir = BCMBAL_TM_SCHED_DIR_US;
+        key.sched_id = get_default_tm_sched_id(nni_intf_id, direction);
+        if (priority > 7) {
+            return BCM_ERR_RANGE;
+        }
+        // There are 8 queues (one per p-bit)
+        key.id = us_fixed_queue_id_list[priority];
+        update_tm_queue_id(access_intf_id, onu_id, uni_id, gemport_id, direction, key.id);
+        // FIXME: The upstream queues have to be created once only.
+        // The upstream queues on the NNI scheduler are shared by all subscribers.
+        // When the first scheduler comes in, the queues get created, and are re-used by all others.
+        // Also, these queues should be present until the last subscriber exits the system.
+        // One solution is to have these queues always, i.e., create it as soon as OLT is enabled.
+    }
+    BCMBAL_CFG_INIT(&cfg, tm_queue, key);
 
-	    bcmbal_tm_queue_cfg queue_cfg;
-	    bcmbal_tm_queue_key queue_key = { };
-	    queue_key.sched_id = mk_sched_id(intf_id, onu_id, "downstream");
-	    queue_key.sched_dir = BCMBAL_TM_SCHED_DIR_DS;
-	    queue_key.id = mk_queue_id(intf_id, onu_id, uni_id, port_no, alloc_id);
+    BCMBAL_CFG_PROP_SET(&cfg, tm_queue, priority, priority);
 
-	    BCMBAL_CFG_INIT(&queue_cfg, tm_queue, queue_key);
+    // BCMBAL_CFG_PROP_SET(&cfg, tm_queue, creation_mode, BCMBAL_TM_CREATION_MODE_MANUAL);
 
-	    err = bcmbal_cfg_clear(DEFAULT_ATERM_ID, &(queue_cfg.hdr));
-	    if (err) {
-		    BCM_LOG(ERROR, openolt_log_id, "Failed to remove downstream tm queue, id %d, sched_id %d, intf_id %d, onu_id %d, uni_id %d, port_no %u, alt_id %d\n",
-				    queue_key.id, queue_key.sched_id, intf_id, onu_id, uni_id, port_no, alloc_id);
-		    return Status(grpc::StatusCode::INTERNAL, "Failed to remove downstream tm queue");
-	    }
 
-        bcmos_fastlock_lock(&flow_lock);
-        port_to_alloc.erase(port_no);
-        bcmos_fastlock_unlock(&flow_lock, 0);
+    err = bcmbal_cfg_set(DEFAULT_ATERM_ID, &cfg.hdr);
+    if (err) {
+        BCM_LOG(ERROR, openolt_log_id, "Failed to create subscriber tm queue, direction = %s, id %d, sched_id %d, \
+                intf_id %d, onu_id %d, uni_id %d\n", \
+                direction.c_str(), key.id, key.sched_id, access_intf_id, onu_id, uni_id);
+        return err;
+    }
 
-	    BCM_LOG(INFO, openolt_log_id, "Remove upstream DBA sched, id %d, sched_id %d, intf_id %d, onu_id %d, uni_id %d, port_no %u, alt_id %d\n",
-			    queue_key.id, queue_key.sched_id, intf_id, onu_id, uni_id, port_no, alloc_id);
+    BCM_LOG(INFO, openolt_log_id, "Created tm_queue, direction %s, id %d, intf_id %d, onu_id %d, uni_id %d", \
+            direction.c_str(), key.id, access_intf_id, onu_id, uni_id);
+
+    return BCM_ERR_OK;
+
+}
+
+Status CreateTrafficQueues_(const tech_profile::TrafficQueues *traffic_queues) {
+    uint32_t intf_id = traffic_queues->intf_id();
+    uint32_t onu_id = traffic_queues->onu_id();
+    uint32_t uni_id = traffic_queues->uni_id();
+    std::string direction;
+    unsigned int alloc_id;
+    bcmos_errno err;
+
+    for (int i = 0; i < traffic_queues->traffic_queues_size(); i++) {
+        tech_profile::TrafficQueue traffic_queue = traffic_queues->traffic_queues(i);
+        if (traffic_queue.direction() == tech_profile::Direction::UPSTREAM) {
+            direction = upstream;
+        } else if (traffic_queue.direction() == tech_profile::Direction::DOWNSTREAM) {
+            direction = downstream;
+        }
+        else {
+            BCM_LOG(ERROR, openolt_log_id, "direction-not-supported %d", traffic_queue.direction());
+            return Status::CANCELLED;
+        }
+        err = CreateQueue(direction, intf_id, onu_id, uni_id, traffic_queue.priority(), traffic_queue.gemport_id());
+        if (err) {
+            return bcm_to_grpc_err(err, "Failed to create queue");
+        }
+    }
+    return Status::OK;
+}
+
+
+bcmos_errno RemoveQueue(std::string direction, uint32_t access_intf_id, uint32_t onu_id, uint32_t uni_id, uint32_t priority,
+                        uint32_t gemport_id) {
+    bcmbal_tm_queue_cfg queue_cfg;
+    bcmbal_tm_queue_key queue_key = { };
+    bcmos_errno err;
+
+    if (direction == downstream) {
+        queue_key.sched_dir = BCMBAL_TM_SCHED_DIR_DS;
+        if (is_tm_queue_id_present(access_intf_id, onu_id, uni_id, gemport_id, direction) && \
+            is_tm_sched_id_present(access_intf_id, onu_id, uni_id, direction)) {
+            queue_key.sched_id = get_tm_sched_id(access_intf_id, onu_id, uni_id, direction);
+            queue_key.id = get_tm_queue_id(access_intf_id, onu_id, uni_id, gemport_id, direction);
+        } else {
+            BCM_LOG(INFO, openolt_log_id, "queue not present in DS. Not clearing");
+            return BCM_ERR_OK;
+        }
+    } else {
+        free_tm_queue_id(access_intf_id, onu_id, uni_id, gemport_id, direction);
+        // In the upstream we use pre-created queues on the NNI scheduler that are used by all subscribers.
+        // They should not be removed. So, lets return OK.
+        return BCM_ERR_OK;
+    }
+
+    BCMBAL_CFG_INIT(&queue_cfg, tm_queue, queue_key);
+
+    err = bcmbal_cfg_clear(DEFAULT_ATERM_ID, &(queue_cfg.hdr));
+
+    if (err) {
+        BCM_LOG(ERROR, openolt_log_id, "Failed to remove queue, direction = %s, id %d, sched_id %d, intf_id %d, onu_id %d, uni_id %d\n",
+                direction.c_str(), queue_key.id, queue_key.sched_id, access_intf_id, onu_id, uni_id);
+        return err;
+    }
+
+    free_tm_queue_id(access_intf_id, onu_id, uni_id, gemport_id, direction);
+
+    return BCM_ERR_OK;
+}
+
+Status RemoveTrafficQueues_(const tech_profile::TrafficQueues *traffic_queues) {
+    uint32_t intf_id = traffic_queues->intf_id();
+    uint32_t onu_id = traffic_queues->onu_id();
+    uint32_t uni_id = traffic_queues->uni_id();
+    uint32_t port_no = traffic_queues->port_no();
+    std::string direction;
+    unsigned int alloc_id;
+    bcmos_errno err;
+
+    for (int i = 0; i < traffic_queues->traffic_queues_size(); i++) {
+        tech_profile::TrafficQueue traffic_queue = traffic_queues->traffic_queues(i);
+        if (traffic_queue.direction() == tech_profile::Direction::UPSTREAM) {
+            direction = upstream;
+        } else if (traffic_queue.direction() == tech_profile::Direction::DOWNSTREAM) {
+            direction = downstream;
+        } else {
+            BCM_LOG(ERROR, openolt_log_id, "direction-not-supported %d", traffic_queue.direction());
+            return Status::CANCELLED;
+        }
+        err = RemoveQueue(direction, intf_id, onu_id, uni_id, traffic_queue.priority(), traffic_queue.gemport_id());
+        if (err) {
+            return bcm_to_grpc_err(err, "Failed to remove queue");
+        }
     }
 
     return Status::OK;
diff --git a/agent/src/indications.cc b/agent/src/indications.cc
index ee35193..30a04ab 100644
--- a/agent/src/indications.cc
+++ b/agent/src/indications.cc
@@ -37,6 +37,7 @@
 
 
 bool subscribed = false;
+uint32_t nni_intf_id = 0;
 
 bcmos_errno OmciIndication(bcmbal_obj *obj);
 
@@ -173,6 +174,9 @@
 
     if (bcm_if_oper_ind->data.new_oper_status == BCMBAL_STATUS_UP) {
         intf_oper_ind->set_oper_state("up");
+        if (bcm_if_oper_ind->key.intf_type == BCMBAL_INTF_TYPE_NNI) {
+            nni_intf_id = bcm_if_oper_ind->key.intf_id;
+        }
     } else {
         intf_oper_ind->set_oper_state("down");
     }
diff --git a/agent/src/indications.h b/agent/src/indications.h
index db064d3..c2f0724 100644
--- a/agent/src/indications.h
+++ b/agent/src/indications.h
@@ -30,5 +30,6 @@
 extern grpc::Status SubscribeIndication();
 extern dev_log_id openolt_log_id;
 extern dev_log_id omci_log_id;
+extern uint32_t nni_intf_id;
 
 #endif
diff --git a/protos/Makefile b/protos/Makefile
index 6a830dc..aa5f8da 100644
--- a/protos/Makefile
+++ b/protos/Makefile
@@ -23,7 +23,7 @@
 GRPC_CPP_PLUGIN = grpc_cpp_plugin
 GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
 
-OBJS = openolt.pb.o openolt.grpc.pb.o ./googleapis/gens/google/api/annotations.grpc.pb.o ./googleapis/gens/google/api/annotations.pb.o ./googleapis/gens/google/api/http.pb.o
+OBJS = tech_profile.pb.o tech_profile.grpc.pb.o openolt.pb.o openolt.grpc.pb.o ./googleapis/gens/google/api/annotations.grpc.pb.o ./googleapis/gens/google/api/annotations.pb.o ./googleapis/gens/google/api/http.pb.o
 
 all: googleapis libopenoltapi.a
 
diff --git a/protos/openolt.proto b/protos/openolt.proto
index ddc8f74..9fa9566 100644
--- a/protos/openolt.proto
+++ b/protos/openolt.proto
@@ -15,6 +15,7 @@
 syntax = "proto3";
 package openolt;
 import "google/api/annotations.proto";
+import "tech_profile.proto";
 
 service Openolt {
 
@@ -130,16 +131,30 @@
         };
     }
 
-    rpc CreateTconts(Tconts) returns (Empty) {
+    rpc CreateTrafficSchedulers(tech_profile.TrafficSchedulers) returns (Empty) {
         option (google.api.http) = {
-            post: "/v1/CreateTconts"
+            post: "/v1/CreateTrafficSchedulers"
             body: "*"
         };
     }
 
-    rpc RemoveTconts(Tconts) returns (Empty) {
+    rpc RemoveTrafficSchedulers(tech_profile.TrafficSchedulers) returns (Empty) {
         option (google.api.http) = {
-            post: "/v1/RemoveTconts"
+            post: "/v1/RemoveTrafficSchedulers"
+            body: "*"
+        };
+    }
+
+    rpc CreateTrafficQueues(tech_profile.TrafficQueues) returns (Empty) {
+        option (google.api.http) = {
+            post: "/v1/CreateTrafficQueues"
+            body: "*"
+        };
+    }
+
+    rpc RemoveTrafficQueues(tech_profile.TrafficQueues) returns (Empty) {
+        option (google.api.http) = {
+            post: "/v1/RemoveTrafficQueues"
             body: "*"
         };
     }
@@ -247,6 +262,7 @@
     fixed32 intf_id = 1;
     fixed32 onu_id = 2;
     fixed32 port_no = 4;
+    fixed32 gemport_id = 5;
     bytes pkt = 3;
 }
 
@@ -464,93 +480,5 @@
     fixed32 onu_id = 2;
 }
 
-enum Direction {
-    UPSTREAM = 0;
-    DOWNSTREAM = 1;
-    BIDIRECTIONAL = 2;
-}
-
-enum SchedulingPolicy {
-    WRR = 0;
-    StrictPriority = 1;
-    Hybrid = 2;
-}
-
-enum AdditionalBW {
-    AdditionalBW_None = 0;
-    AdditionalBW_NA = 1;
-    AdditionalBW_BestEffort = 2;
-    AdditionalBW_Auto = 3;
-}
-
-enum DiscardPolicy {
-    TailDrop = 0;
-    WTailDrop = 1;
-    Red = 2;
-    WRed = 3;
-}
-
-enum InferredAdditionBWIndication {
-    InferredAdditionBWIndication_None = 0;
-    InferredAdditionBWIndication_Assured = 1;
-    InferredAdditionBWIndication_BestEffort = 2;
-}
-
-message Scheduler {
-    Direction direction = 1;
-    AdditionalBW additional_bw = 2; // Valid on for “direction == Upstream”.
-    fixed32 priority = 3;
-    fixed32 weight = 4;
-    SchedulingPolicy sched_policy = 5;
-}
-
-message TrafficShapingInfo {
-    fixed32 cir = 1;
-    fixed32 cbs = 2;
-    fixed32 pir = 3;
-    fixed32 pbs = 4;
-    fixed32 gir = 5; // only if “direction == Upstream ”
-    InferredAdditionBWIndication add_bw_ind = 6; // only if “direction == Upstream”
-}
-
-message Tcont {
-    Direction direction = 1;
-    fixed32 alloc_id = 2; // valid only if “direction == Upstream ”
-    Scheduler scheduler = 3;
-    TrafficShapingInfo traffic_shaping_info = 4;
-}
-
-message Tconts {
-    fixed32 intf_id = 1;
-    fixed32 onu_id = 2;
-    fixed32 uni_id = 4;
-    fixed32 port_no = 5;
-    repeated Tcont tconts = 3;
-}
-
-message TailDropDiscardConfig {
-    fixed32 queue_size = 1;
-}
-
-message RedDiscardConfig {
-    fixed32 min_threshold = 1;
-    fixed32 max_threshold = 2;
-    fixed32 max_probability = 3;
-}
-
-message WRedDiscardConfig {
-    RedDiscardConfig green = 1;
-    RedDiscardConfig yellow = 2;
-    RedDiscardConfig red = 3;
-}
-
-message DiscardConfig {
-    DiscardPolicy discard_policy = 1;
-    oneof discard_config {
-        TailDropDiscardConfig tail_drop_discard_config = 2;
-        RedDiscardConfig red_discard_config = 3;
-        WRedDiscardConfig wred_discard_config = 4;
-    }
-}
-
 message Empty {}
+
diff --git a/protos/tech_profile.proto b/protos/tech_profile.proto
new file mode 100644
index 0000000..1b98e2d
--- /dev/null
+++ b/protos/tech_profile.proto
@@ -0,0 +1,126 @@
+// Copyright (c) 2018 Open Networking Foundation
+//
+// 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.
+
+syntax = "proto3";
+package tech_profile;
+import "google/api/annotations.proto";
+
+enum Direction {
+    UPSTREAM = 0;
+    DOWNSTREAM = 1;
+    BIDIRECTIONAL = 2;
+}
+
+enum SchedulingPolicy {
+    WRR = 0;
+    StrictPriority = 1;
+    Hybrid = 2;
+}
+
+enum AdditionalBW {
+    AdditionalBW_None = 0;
+    AdditionalBW_NA = 1;
+    AdditionalBW_BestEffort = 2;
+    AdditionalBW_Auto = 3;
+}
+
+enum DiscardPolicy {
+    TailDrop = 0;
+    WTailDrop = 1;
+    Red = 2;
+    WRed = 3;
+}
+
+enum InferredAdditionBWIndication {
+    InferredAdditionBWIndication_None = 0;
+    InferredAdditionBWIndication_Assured = 1;
+    InferredAdditionBWIndication_BestEffort = 2;
+}
+
+message SchedulerConfig {
+    Direction direction = 1;
+    AdditionalBW additional_bw = 2; // Valid on for “direction == Upstream”.
+    fixed32 priority = 3;
+    fixed32 weight = 4;
+    SchedulingPolicy sched_policy = 5;
+}
+
+message TrafficShapingInfo {
+    fixed32 cir = 1;
+    fixed32 cbs = 2;
+    fixed32 pir = 3;
+    fixed32 pbs = 4;
+    fixed32 gir = 5; // only if “direction == Upstream ”
+    InferredAdditionBWIndication add_bw_ind = 6; // only if “direction == Upstream”
+}
+
+message TrafficScheduler {
+    Direction direction = 1;
+    fixed32 alloc_id = 2; // valid only if “direction == Upstream ”
+    SchedulerConfig scheduler = 3;
+    TrafficShapingInfo traffic_shaping_info = 4;
+}
+
+message TrafficSchedulers {
+    fixed32 intf_id = 1;
+    fixed32 onu_id = 2;
+    fixed32 uni_id = 4;
+    fixed32 port_no = 5;
+    repeated TrafficScheduler traffic_scheds = 3;
+}
+
+message TailDropDiscardConfig {
+    fixed32 queue_size = 1;
+}
+
+message RedDiscardConfig {
+    fixed32 min_threshold = 1;
+    fixed32 max_threshold = 2;
+    fixed32 max_probability = 3;
+}
+
+message WRedDiscardConfig {
+    RedDiscardConfig green = 1;
+    RedDiscardConfig yellow = 2;
+    RedDiscardConfig red = 3;
+}
+
+message DiscardConfig {
+    DiscardPolicy discard_policy = 1;
+    oneof discard_config {
+        TailDropDiscardConfig tail_drop_discard_config = 2;
+        RedDiscardConfig red_discard_config = 3;
+        WRedDiscardConfig wred_discard_config = 4;
+    }
+}
+
+message TrafficQueue {
+    Direction direction = 1;
+    fixed32 gemport_id = 2;
+    string pbit_map = 3;
+    bool aes_encryption = 4;
+    SchedulingPolicy sched_policy = 5;
+    fixed32 priority = 6;
+    fixed32 weight = 7;
+    DiscardPolicy discard_policy = 8;
+    DiscardConfig discard_config = 9;
+}
+
+message TrafficQueues {
+    fixed32 intf_id = 1;
+    fixed32 onu_id = 2;
+    fixed32 uni_id = 4;
+    fixed32 port_no = 5;
+    repeated TrafficQueue traffic_queues = 6;
+}