Only remove TMAC flow when it is the last port within the same VLAN if TMAC doesn't support in_port matching

Change-Id: Iafb72153eddd126c5598960a46130c50ddfe3cbd
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 17fe16e..9509f82 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -822,15 +822,33 @@
             .addCondition(Criteria.matchEthDst(deviceMac))
             .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
 
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
         if (pushVlan) {
             fob.addCondition(Criteria.matchVlanId(VlanId.NONE));
-            TrafficTreatment tt = DefaultTrafficTreatment.builder()
-                    .pushVlan().setVlanId(vlanId).build();
-            fob.withMeta(tt);
+            tBuilder.pushVlan().setVlanId(vlanId);
         } else {
             fob.addCondition(Criteria.matchVlanId(vlanId));
         }
 
+        // NOTE: Some switch hardware share the same filtering flow among different ports.
+        //       We use this metadata to let the driver know that there is no more enabled port
+        //       within the same VLAN on this device.
+        boolean noMoreEnabledPort = srManager.interfaceService.getInterfaces().stream()
+                .filter(intf -> intf.connectPoint().deviceId().equals(deviceId))
+                .filter(intf -> intf.vlanTagged().contains(vlanId) ||
+                        intf.vlanUntagged().equals(vlanId) ||
+                        intf.vlanNative().equals(vlanId))
+                .noneMatch(intf -> {
+                    Port port = srManager.deviceService.getPort(intf.connectPoint());
+                    return port != null && port.isEnabled();
+                });
+        if (noMoreEnabledPort) {
+            tBuilder.wipeDeferred();
+        }
+
+        fob.withMeta(tBuilder.build());
+
         fob.permit().fromApp(srManager.appId);
         return fob;
     }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
index 1fdf2ab..430d424 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
@@ -407,12 +407,11 @@
 
         VlanId assignedVlan = null;
         if (vidCriterion != null) {
-            // Use the VLAN in metadata whenever a metadata is provided
-            if (filt.meta() != null) {
-                assignedVlan = readVlanFromTreatment(filt.meta());
             // Use the VLAN in criterion if metadata is not present and the traffic is tagged
-            } else if (!vidCriterion.vlanId().equals(VlanId.NONE)) {
+            if (!vidCriterion.vlanId().equals(VlanId.NONE)) {
                 assignedVlan = vidCriterion.vlanId();
+            } else if (filt.meta() != null) {
+                assignedVlan = readVlanFromTreatment(filt.meta());
             }
 
             if (assignedVlan == null) {
@@ -434,7 +433,18 @@
                                                          applicationId)) {
                 log.trace("{} MAC filtering rules in TMAC table: {} for dev: {}",
                           (install) ? "adding" : "removing", tmacRule, deviceId);
-                ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
+
+                if (install) {
+                    ops = ops.add(tmacRule);
+                } else {
+                    // NOTE: Only remove TMAC flow when there is no more enabled port within the
+                    // same VLAN on this device if TMAC doesn't support matching on in_port.
+                    if (matchInPortTmacTable() || (filt.meta() != null && filt.meta().clearedDeferred())) {
+                        ops = ops.remove(tmacRule);
+                    } else {
+                        log.debug("Abort TMAC flow removal on {}. Some other ports still share this TMAC flow");
+                    }
+                }
             }
         }
 
@@ -703,11 +713,11 @@
     /**
      * Builds TMAC rules for IPv4 packets.
      *
-     * @param ethCriterion
-     * @param vidCriterion
-     * @param ofdpaMatchVlanVid
-     * @param applicationId
-     * @param pnum
+     * @param ethCriterion dst mac matching
+     * @param vidCriterion vlan id assigned to the port
+     * @param ofdpaMatchVlanVid OFDPA vlan id matching
+     * @param applicationId application id
+     * @param pnum port number
      * @return TMAC rule for IPV4 packets
      */
     private FlowRule buildTmacRuleForIpv4(EthCriterion ethCriterion,
@@ -748,11 +758,11 @@
     /**
      * Builds TMAC rule for MPLS packets.
      *
-     * @param ethCriterion
-     * @param vidCriterion
-     * @param ofdpaMatchVlanVid
-     * @param applicationId
-     * @param pnum
+     * @param ethCriterion dst mac matching
+     * @param vidCriterion vlan id assigned to the port
+     * @param ofdpaMatchVlanVid OFDPA vlan id matching
+     * @param applicationId application id
+     * @param pnum port number
      * @return TMAC rule for MPLS packets
      */
     private FlowRule buildTmacRuleForMpls(EthCriterion ethCriterion,
@@ -793,11 +803,11 @@
     /**
      * Builds TMAC rules for IPv6 packets.
      *
-     * @param ethCriterion
-     * @param vidCriterion
-     * @param ofdpaMatchVlanVid
-     * @param applicationId
-     * @param pnum
+     * @param ethCriterion dst mac matching
+     * @param vidCriterion vlan id assigned to the port
+     * @param ofdpaMatchVlanVid OFDPA vlan id matching
+     * @param applicationId application id
+     * @param pnum port number
      * @return TMAC rule for IPV6 packets
      */
      private FlowRule buildTmacRuleForIpv6(EthCriterion ethCriterion,