This patch contains classification Linux generic implementation.
This patch incorporates review comments from Maxim.

Signed-off-by: Balasubramanian Manoharan <[email protected]>
---
 .../linux-generic/include/api/odp_classification.h |  11 +-
 .../include/odp_classification_inlines.h           | 263 ++++++
 .../include/odp_classification_internal.h          | 205 +++++
 .../include/odp_classification_internal_function.h | 168 ++++
 platform/linux-generic/include/odp_internal.h      |   2 +
 platform/linux-generic/odp_classification.c        | 951 ++++++++++++++++++---
 6 files changed, 1476 insertions(+), 124 deletions(-)
 create mode 100644 platform/linux-generic/include/odp_classification_inlines.h
 create mode 100644 platform/linux-generic/include/odp_classification_internal.h
 create mode 100644 
platform/linux-generic/include/odp_classification_internal_function.h

diff --git a/platform/linux-generic/include/api/odp_classification.h 
b/platform/linux-generic/include/api/odp_classification.h
index cc5d84a..82d6ffe 100644
--- a/platform/linux-generic/include/api/odp_classification.h
+++ b/platform/linux-generic/include/api/odp_classification.h
@@ -48,6 +48,10 @@ typedef uint32_t odp_flowsig_t;
 */
 #define ODP_COS_INVALID    ((odp_cos_t)~0)
 
+
+/** Maximum ClassOfService name lenght in chars */
+#define ODP_COS_NAME_LEN 32
+
 /**
  * Class-of-service packet drop policies
  */
@@ -325,7 +329,7 @@ typedef uint32_t odp_pmr_t;
 /**
  * Macro for Invalid PMR.
  */
-#define    ODP_PMR_INVAL ((odp_pmr_t)NULL)
+#define    ODP_PMR_INVAL ((odp_pmr_t)~0)
 
 /**
  * Packet Matching Rule field enumeration
@@ -497,9 +501,6 @@ typedef uint32_t odp_pmr_set_t;
  * @param[in]  num_terms       Number of terms in the match rule.
  * @param[in]  terms           Array of num_terms entries, one entry per
  *                             term desired.
- * @param[in]  dst_cos         Class-of-service to be assigned to packets
- *                             that match the compound rule-set,
- *                             or a subset thereof, if partly applied.
  * @param[out] pmr_set_id      Returned handle to the composite rule set.
  *
  * @return                     Return value may be a positive number
@@ -510,7 +511,7 @@ typedef uint32_t odp_pmr_set_t;
  *                             or -1 for error.
  */
 int odp_pmr_match_set_create(int num_terms, odp_pmr_match_t *terms,
-                            odp_cos_t dst_cos, odp_pmr_set_t *pmr_set_id);
+                            odp_pmr_set_t *pmr_set_id);
 
 /**
  * Function to delete a composite packet match rule set
diff --git a/platform/linux-generic/include/odp_classification_inlines.h 
b/platform/linux-generic/include/odp_classification_inlines.h
new file mode 100644
index 0000000..9bc6bf3
--- /dev/null
+++ b/platform/linux-generic/include/odp_classification_inlines.h
@@ -0,0 +1,263 @@
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+
+/**
+ * @file
+ *
+ * ODP Classification Inlines
+ * Classification Inlines Functions
+ */
+#ifndef __ODP_CLASSIFICATION_INLINES__
+#define __ODP_CLASSIFICATION_INLINES__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_debug.h>
+#include <odph_eth.h>
+#include <odph_ip.h>
+#include <odph_udp.h>
+#include <odph_tcp.h>
+
+/* PMR term value verification function
+These functions verify the given PMR term value with the value in the packet
+These following functions return 1 on success and 0 on failure
+*/
+
+static inline int verify_pmr_packet_len(odp_packet_hdr_t *pkt_hdr,
+                                       pmr_term_value_t *term_value)
+{
+       if (term_value->match_type == ODP_PMR_MASK) {
+               if (term_value->mask.val == (pkt_hdr->frame_len &
+                   term_value->mask.mask))
+                       return 1;
+       } else {
+               if ((term_value->range.val1 <= pkt_hdr->frame_len) &&
+                   (pkt_hdr->frame_len <= term_value->range.val2))
+                       return 1;
+       }
+       return 0;
+}
+static inline int verify_pmr_ip_proto(uint8_t *pkt_addr,
+                                     odp_packet_hdr_t *pkt_hdr,
+                                     pmr_term_value_t *term_value)
+{
+       if (!pkt_hdr->input_flags.ipv4)
+               return 0;
+       uint8_t proto = ((odph_ipv4hdr_t *)(pkt_addr +
+                       pkt_hdr->l3_offset))->proto;
+       if (term_value->match_type == ODP_PMR_MASK) {
+               if (term_value->mask.val == (proto & term_value->mask.mask))
+                       return 1;
+       } else {
+               if ((term_value->range.val1 <= proto) &&
+                   (proto <= term_value->range.val2))
+                       return 1;
+       }
+       return 0;
+}
+
+static inline int verify_pmr_ipv4_saddr(uint8_t *pkt_addr,
+                                       odp_packet_hdr_t *pkt_hdr,
+                                       pmr_term_value_t *term_value)
+{
+       if (!pkt_hdr->input_flags.ipv4)
+               return 0;
+       uint32_t ipaddr = odp_be_to_cpu_32(((odph_ipv4hdr_t *)(pkt_addr +
+                       pkt_hdr->l3_offset))->src_addr);
+       if (term_value->match_type == ODP_PMR_MASK) {
+               if (term_value->mask.val == (ipaddr & term_value->mask.mask))
+                       return 1;
+       } else {
+               if ((term_value->range.val1 <= ipaddr) &&
+                   (ipaddr <= term_value->range.val2))
+                       return 1;
+       }
+       return 0;
+}
+
+static inline int verify_pmr_ipv4_daddr(uint8_t *pkt_addr,
+                                       odp_packet_hdr_t *pkt_hdr,
+                                       pmr_term_value_t *term_value)
+{
+       if (!pkt_hdr->input_flags.ipv4)
+               return 0;
+       uint32_t ipaddr = odp_be_to_cpu_32(((odph_ipv4hdr_t *)(pkt_addr +
+                       pkt_hdr->l3_offset))->dst_addr);
+       if (term_value->match_type == ODP_PMR_MASK) {
+               if (term_value->mask.val == (ipaddr & term_value->mask.mask))
+                       return 1;
+       } else {
+               if ((term_value->range.val1 <= ipaddr) &&
+                   (ipaddr <= term_value->range.val2))
+                       return 1;
+       }
+       return 0;
+}
+
+static inline int verify_pmr_tcp_sport(uint8_t *pkt_addr,
+                                      odp_packet_hdr_t *pkt_hdr,
+                                      pmr_term_value_t *term_value)
+{
+       if (!pkt_hdr->input_flags.tcp)
+               return 0;
+       uint16_t sport = odp_be_to_cpu_16(((odph_tcphdr_t *)(pkt_addr +
+                       pkt_hdr->l4_offset))->src_port);
+       if (term_value->match_type == ODP_PMR_MASK) {
+               if (term_value->mask.val == (sport & term_value->mask.mask))
+                       return 1;
+       } else {
+               if ((term_value->range.val1 <= sport) &&
+                   (sport <= term_value->range.val2))
+                       return 1;
+       }
+       return 0;
+}
+
+static inline int verify_pmr_tcp_dport(uint8_t *pkt_addr,
+                                      odp_packet_hdr_t *pkt_hdr,
+                                      pmr_term_value_t *term_value)
+{
+       if (!pkt_hdr->input_flags.tcp)
+               return 0;
+       uint16_t dport = odp_be_to_cpu_16(((odph_tcphdr_t *)(pkt_addr +
+                       pkt_hdr->l4_offset))->dst_port);
+       if (term_value->match_type == ODP_PMR_MASK) {
+               if (term_value->mask.val == (dport & term_value->mask.mask))
+                       return 1;
+       } else {
+               if ((term_value->range.val1 <= dport) &&
+                   (dport <= term_value->range.val2))
+                       return 1;
+       }
+       return 0;
+}
+
+static inline int verify_pmr_udp_dport(uint8_t *pkt_addr,
+                                      odp_packet_hdr_t *pkt_hdr,
+                                      pmr_term_value_t *term_value)
+{
+       if (!pkt_hdr->input_flags.udp)
+               return 0;
+       uint16_t dport = odp_be_to_cpu_16(((odph_udphdr_t *)(pkt_addr +
+                       pkt_hdr->l4_offset))->dst_port);
+       if (term_value->match_type == ODP_PMR_MASK) {
+               if (term_value->mask.val == (dport & term_value->mask.mask))
+                       return 1;
+       } else {
+               if ((term_value->range.val1 <= dport) &&
+                   (dport <= term_value->range.val2))
+                       return 1;
+       }
+       return 0;
+}
+static inline int verify_pmr_udp_sport(uint8_t *pkt_addr,
+                                      odp_packet_hdr_t *pkt_hdr,
+                                      pmr_term_value_t *term_value)
+{
+       if (!pkt_hdr->input_flags.udp)
+               return 0;
+       uint16_t sport = odp_be_to_cpu_16(((odph_udphdr_t *)(pkt_addr +
+                       pkt_hdr->l4_offset))->src_port);
+       if (term_value->match_type == ODP_PMR_MASK) {
+               if (term_value->mask.val == (sport & term_value->mask.mask))
+                       return 1;
+       } else {
+               if ((term_value->range.val1 <= sport) &&
+                   (sport <= term_value->range.val2))
+                       return 1;
+       }
+       return 0;
+}
+
+static inline int verify_pmr_dmac(uint8_t *pkt_addr,
+                                 odp_packet_hdr_t *pkt_hdr,
+                                 pmr_term_value_t *term_value)
+{
+       (void)pkt_addr;
+       (void)pkt_hdr;
+       (void)term_value;
+       return 0;
+}
+
+static inline int verify_pmr_ipv6_saddr(uint8_t *pkt_addr,
+                                       odp_packet_hdr_t *pkt_hdr,
+                                       pmr_term_value_t *term_value)
+{
+       (void)pkt_addr;
+       (void)pkt_hdr;
+       (void)term_value;
+       return 0;
+}
+static inline int verify_pmr_ipv6_daddr(uint8_t *pkt_addr,
+                                       odp_packet_hdr_t *pkt_hdr,
+                                       pmr_term_value_t *term_value)
+{
+       (void)pkt_addr;
+       (void)pkt_hdr;
+       (void)term_value;
+       return 0;
+}
+static inline int verify_pmr_vlan_id_0(uint8_t *pkt_addr,
+                                      odp_packet_hdr_t *pkt_hdr,
+                                      pmr_term_value_t *term_value)
+{
+       (void)pkt_addr;
+       (void)pkt_hdr;
+       (void)term_value;
+       return 0;
+}
+static inline int verify_pmr_vlan_id_x(uint8_t *pkt_addr,
+                                      odp_packet_hdr_t *pkt_hdr,
+                                      pmr_term_value_t *term_value)
+{
+       (void)pkt_addr;
+       (void)pkt_hdr;
+       (void)term_value;
+       return 0;
+}
+static inline int verify_pmr_ipsec_spi(uint8_t *pkt_addr,
+                                      odp_packet_hdr_t *pkt_hdr,
+                                      pmr_term_value_t *term_value)
+{
+       (void)pkt_addr;
+       (void)pkt_hdr;
+       (void)term_value;
+       return 0;
+}
+static inline int verify_pmr_ld_vni(uint8_t *pkt_addr,
+                                   odp_packet_hdr_t *pkt_hdr,
+                                   pmr_term_value_t *term_value)
+{
+       (void)pkt_addr;
+       (void)pkt_hdr;
+       (void)term_value;
+       return 0;
+}
+static inline int verify_pmr_eth_type_0(uint8_t *pkt_addr,
+                                       odp_packet_hdr_t *pkt_hdr,
+                                       pmr_term_value_t *term_value)
+{
+       (void)pkt_addr;
+       (void)pkt_hdr;
+       (void)term_value;
+       return 0;
+}
+static inline int verify_pmr_eth_type_x(uint8_t *pkt_addr,
+                                       odp_packet_hdr_t *pkt_hdr,
+                                       pmr_term_value_t *term_value)
+{
+       (void)pkt_addr;
+       (void)pkt_hdr;
+       (void)term_value;
+       return 0;
+}
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/platform/linux-generic/include/odp_classification_internal.h 
b/platform/linux-generic/include/odp_classification_internal.h
new file mode 100644
index 0000000..97f3577
--- /dev/null
+++ b/platform/linux-generic/include/odp_classification_internal.h
@@ -0,0 +1,205 @@
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+
+/**
+ * @file
+ *
+ * ODP Classification Internal
+ * Describes the classification internal data model
+ */
+
+#ifndef ODP_CLASSIFICATION_INTERNAL_H_
+#define ODP_CLASSIFICATION_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_rwlock.h>
+#include <odp_classification.h>
+#include <odp_buffer_pool_internal.h>
+#include <odp_packet_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp_queue_internal.h>
+
+/** This version uses rwlock for synchronization this should be modified
+into atomic variables once the changes to atomic variable patch has been
+accepted in linux-generic repo**/
+
+/** The following values needs to be change into configs**/
+/* */
+/* Maximum Class Of Service Entry */
+#define ODP_COS_MAX_ENTRY              64
+/* Maximum PMR Set Entry */
+#define ODP_PMRSET_MAX_ENTRY           64
+/* Maximum PMR Entry */
+#define ODP_PMR_MAX_ENTRY              64
+/* Maximum PMR Terms in a PMR Set */
+#define ODP_PMRTERM_MAX                        8
+/* Maximum PMRs attached in PKTIO Level */
+#define ODP_PKTIO_MAX_PMR              8
+/* L2 Priority Bits */
+#define ODP_COS_L2_QOS_BITS            3
+/* Max L2 QoS value */
+#define ODP_COS_MAX_L2_QOS             (1 << ODP_COS_L2_QOS_BITS)
+/* L2 DSCP Bits */
+#define ODP_COS_L3_QOS_BITS            6
+/* Max L3 QoS Value */
+#define ODP_COS_MAX_L3_QOS             (1 << ODP_COS_L3_QOS_BITS)
+/* Max PMR Term bits */
+#define ODP_PMR_TERM_BITS_MAX          64
+
+/* forward declaration */
+typedef union pmr_u pmr_t;
+
+/**
+Packet Matching Rule Term Value
+
+Stores the Term and Value mapping for a PMR.
+The maximum size of value currently supported in 64 bits
+**/
+typedef struct pmr_term_value {
+       odp_pmr_match_type_e match_type; /**< Packet Match Type*/
+       odp_pmr_term_e  term;           /* PMR Term */
+       union {
+               struct {
+                       uint64_t        val;
+                       uint64_t        mask;
+               } mask; /**< Match a masked set of bits */
+               struct {
+                       uint64_t        val1;
+                       uint64_t        val2;
+               } range; /**< Match an integer range */
+       };
+} pmr_term_value_t;
+
+/*
+Class Of Service
+*/
+struct cos_s {
+       odp_rwlock_t lock;              /* cos rwlock */
+       queue_entry_t *queue;           /* Associated Queue */
+       odp_queue_group_t queue_group;  /* Associated Queue Group */
+       pool_entry_t *pool;             /* Associated Buffer pool */
+       pmr_t *pmr;                     /* Associated PMR */
+       bool valid;                     /* validity Flag */
+       odp_drop_e drop_policy;         /* Associated Drop Policy */
+       odp_cos_flow_set_t flow_set;    /* Assigned Flow Set */
+       char name[ODP_COS_NAME_LEN];    /* name */
+       size_t headroom;                /* Headroom for this CoS */
+};
+
+typedef union cos_u {
+       struct cos_s s;
+       uint8_t pad[ODP_CACHE_LINE_SIZE_ROUNDUP(sizeof(struct cos_s))];
+} cos_t;
+
+
+/**
+Packet Matching Rule
+
+**/
+struct pmr_s {
+       odp_rwlock_t lock;              /* pmr rwlock*/
+       cos_t *cos;                     /* Associated CoS */
+       odp_atomic_u32_t count;         /* num of packets matching this rule */
+       uint16_t num_pmr;               /* num of PMR Term Values*/
+       bool valid;                     /* Validity Flag */
+       pmr_term_value_t  pmr_term_value[1];    /* Associated PMR Term */
+};
+
+typedef union pmr_u {
+       struct pmr_s s;
+       uint8_t pad[ODP_CACHE_LINE_SIZE_ROUNDUP(sizeof(struct pmr_s))];
+} pmr_t;
+
+/**
+Packet Matching Rule Set
+
+This structure is implemented as a extension over struct pmr_s
+In order to use same pointer to access both pmr_s and pmr_set_s
+'num_pmr' value is used to differentiate between pmr_s and pmr_set_s struct
+**/
+struct pmr_set_s {
+       pmr_t pmr;
+       pmr_term_value_t  pmr_term_value[ODP_PMRTERM_MAX - 1];
+                       /* List of associated PMR Terms */
+};
+
+typedef union pmr_set_u {
+       struct pmr_set_s s;
+       uint8_t pad[ODP_CACHE_LINE_SIZE_ROUNDUP(sizeof(struct pmr_set_s))];
+} pmr_set_t;
+
+/**
+L2 QoS and CoS Map
+
+This structure holds the mapping between L2 QoS value and
+corresponding cos_t object
+**/
+typedef struct pmr_l2_cos {
+       odp_rwlock_t lock;      /* pmr_l2_cos rwlock */
+       cos_t *cos[ODP_COS_MAX_L2_QOS]; /* Array of CoS objects */
+} pmr_l2_cos_t;
+
+/**
+L3 QoS and CoS Map
+
+This structure holds the mapping between L3 QoS value and
+corresponding cos_t object
+**/
+typedef struct pmr_l3_cos {
+       odp_rwlock_t lock;      /* pmr_l3_cos rwlock */
+       cos_t *cos[ODP_COS_MAX_L3_QOS]; /* Array of CoS objects */
+} pmr_l3_cos_t;
+
+/**
+Linux Generic Classifier
+
+This structure is stored in pktio_entry and holds all
+the classifier configuration value.
+**/
+typedef struct classifier {
+       odp_rwlock_t lock;              /*pktio_cos rwlock */
+       uint8_t num_pmr;                /* num of PMRs linked to given PKTIO*/
+       bool l3_precedence;             /* L3 QoS precedence */
+       odp_cos_flow_set_t flow_set;    /* Flow Set to be calculated
+                                       for this pktio */
+       pmr_l2_cos_t l2_cos_table;      /* L2 QoS-CoS table map */
+       pmr_l3_cos_t l3_cos_table;      /* L3 Qos-CoS table map */
+       pmr_t *pmr[ODP_PKTIO_MAX_PMR];  /* PMRs linked with this PKTIO */
+       cos_t *error_cos;               /* Associated Error CoS */
+       cos_t *default_cos;             /* Associated Default CoS */
+       size_t headroom;                /* Pktio Headroom */
+       size_t skip;                    /* Pktio Skip Offset */
+} classifier_t;
+
+/**
+Class of Service Table
+**/
+typedef struct odp_cos_table {
+       cos_t cos_entry[ODP_COS_MAX_ENTRY];
+} cos_tbl_t;
+
+/**
+PMR set table
+**/
+typedef struct pmr_set_tbl {
+       pmr_set_t pmr_set[ODP_PMRSET_MAX_ENTRY];
+} pmr_set_tbl_t;
+
+/**
+PMR table
+**/
+typedef struct pmr_tbl {
+       pmr_t pmr[ODP_PMR_MAX_ENTRY];
+} pmr_tbl_t;
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git 
a/platform/linux-generic/include/odp_classification_internal_function.h 
b/platform/linux-generic/include/odp_classification_internal_function.h
new file mode 100644
index 0000000..f625093
--- /dev/null
+++ b/platform/linux-generic/include/odp_classification_internal_function.h
@@ -0,0 +1,168 @@
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+
+/**
+ * @file
+ *
+ * ODP Classification Internal Functions
+ * Describes the classification internal Functions
+ */
+
+#ifndef __ODP_CLASSIFICATION_INTERNAL_FUNCTION__
+#define __ODP_CLASSIFICATION_INTERNAL_FUNCTION__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_classification.h>
+#include <odp_queue.h>
+#include <odp_packet_internal.h>
+#include <odp_packet_io.h>
+#include <odp_packet_io_internal.h>
+#include <odp_classification_internal.h>
+
+/** Classification Internal function **/
+
+/**
+@internal
+Select a CoS for the given Packet based on pktio
+
+This function will call all the PMRs associated with a pktio for
+a given packet and will return the matched COS object.
+This function will check PMR, L2 and L3 QoS COS object associated
+with the PKTIO interface.
+
+Returns the default cos if the packet does not match any PMR
+Returns the error_cos if the packet has an error
+**/
+cos_t *pktio_select_cos(pktio_entry_t *pktio, uint8_t *pkt_addr,
+                      odp_packet_hdr_t *pkt_hdr);
+
+/**
+@internal
+match_qos_cos
+
+Select a CoS for the given Packet based on QoS values
+This function returns the COS object matching the L2 and L3 QoS
+based on the l3_preference value of the pktio
+**/
+cos_t *match_qos_cos(pktio_entry_t *entry, uint8_t *pkt_addr,
+                    odp_packet_hdr_t *hdr);
+/**
+Packet Classifier
+
+Start function for Packet Classifier
+This function calls Classifier module internal functions for a given packet and
+enqueues the packet to specific Queue based on PMR and CoS selected.
+**/
+int packet_classifier(odp_pktio_t pktio, odp_packet_t pkt, size_t len,
+                     size_t frame_offset);
+
+
+/**
+@internal
+match_pmr_cos
+
+Match a PMR chain with a Packet and return matching CoS
+This function gets called recursively to check the chained PMR Term value
+with the packet.
+
+**/
+cos_t *match_pmr_cos(cos_t *cos, uint8_t *pkt_addr, pmr_t *pmr,
+                    odp_packet_hdr_t *hdr);
+/**
+@internal
+CoS associated with L3 QoS value
+
+This function returns the CoS associated with L3 QoS value
+**/
+cos_t *match_qos_l3_cos(pmr_l3_cos_t *l3_cos, uint8_t *pkt_addr,
+                       odp_packet_hdr_t *hdr);
+
+/**
+@internal
+CoS associated with L2 QoS value
+
+This function returns the CoS associated with L2 QoS value
+**/
+cos_t *match_qos_l2_cos(pmr_l2_cos_t *l2_cos, uint8_t *pkt_addr,
+                       odp_packet_hdr_t *hdr);
+/**
+@internal
+Flow Signature Calculation
+
+This function calculates the Flow Signature for a packet based on
+CoS and updates in Packet Meta Data
+**/
+int update_flow_signature(uint8_t *pkt_addr, cos_t *cos);
+
+/**
+@internal
+Allocate a odp_pmr_set_t Handle
+*/
+odp_pmr_set_t alloc_pmr_set(pmr_t **pmr);
+
+/**
+@internal
+Allocate a odp_pmr_t Handle
+*/
+odp_pmr_t alloc_pmr(pmr_t **pmr);
+
+/**
+@internal
+Pointer to pmr_set_t Handle
+This function checks for validity of pmr_set_t Handle
+*/
+pmr_set_t *get_pmr_set_entry(odp_pmr_set_t pmr_set_id);
+
+/**
+@internal
+Pointer to pmr_set_t Handle
+*/
+pmr_set_t *get_pmr_set_entry_internal(odp_pmr_set_t pmr_set_id);
+
+/**
+@internal
+Pointer to pmr_set_t Handle
+This function checks for validity of pmr_set_t Handle
+*/
+pmr_t *get_pmr_entry(odp_pmr_t pmr_id);
+
+/**
+@internal
+Pointer to pmr_set_t Handle
+*/
+pmr_t *get_pmr_entry_internal(odp_pmr_t pmr_id);
+
+/**
+@internal
+Pointer to odp_cos_t Handle
+*/
+cos_t *get_cos_entry(odp_cos_t cos_id);
+
+/**
+@internal
+Pointer to odp_cos_t Handle
+This function checks for validity of odp_cos_t Handle
+*/
+cos_t *get_cos_entry_internal(odp_cos_t cos_id);
+
+/**
+@internal
+Verify PMR with a Packet
+
+This function goes through each PMR_TERM value in pmr_t structure and
+calls verification function for each term.Returns 1 if PMR matches or 0
+Otherwise.
+**/
+int verify_pmr(pmr_t *pmr, uint8_t *pkt_addr, odp_packet_hdr_t *pkt_hdr);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/platform/linux-generic/include/odp_internal.h 
b/platform/linux-generic/include/odp_internal.h
index f8c1596..04c1030 100644
--- a/platform/linux-generic/include/odp_internal.h
+++ b/platform/linux-generic/include/odp_internal.h
@@ -32,6 +32,8 @@ int odp_buffer_pool_init_global(void);
 int odp_pktio_init_global(void);
 int odp_pktio_init_local(void);
 
+int odp_classification_init_global(void);
+
 int odp_queue_init_global(void);
 
 int odp_crypto_init_global(void);
diff --git a/platform/linux-generic/odp_classification.c 
b/platform/linux-generic/odp_classification.c
index dbc74e2..21c47b4 100644
--- a/platform/linux-generic/odp_classification.c
+++ b/platform/linux-generic/odp_classification.c
@@ -2,236 +2,949 @@
 #include <odp_align.h>
 #include <odp_queue.h>
 #include <odp_debug.h>
+#include <odp_internal.h>
 #include <odp_debug_internal.h>
+#include <odp_packet_internal.h>
 #include <odp_packet_io.h>
+#include <odp_packet_io_internal.h>
+#include <odp_classification_inlines.h>
+#include <odp_classification_internal.h>
+#include <odp_classification_internal_function.h>
+#include <odp_buffer_pool_internal.h>
+#include <odp_shared_memory.h>
+#include <odph_eth.h>
+#include <string.h>
+#include <odp_rwlock.h>
 
-odp_cos_t odp_cos_create(const char *name)
+/** This version of classifier uses rwlock for synchronization
+This should be modified to use odp_atomic variables once
+odp_atomic patch are stable and included into linux-generic repo**/
+
+#define WRITE_LOCK(a)      odp_rwlock_write_lock(a)
+#define WRITE_UNLOCK(a)    odp_rwlock_write_unlock(a)
+#define LOCK_INIT(a) odp_rwlock_init(a)
+
+#define READ_LOCK(a)      odp_rwlock_write_lock(a)
+#define READ_UNLOCK(a)    odp_rwlock_write_unlock(a)
+
+static cos_tbl_t *cos_tbl;
+static pmr_set_tbl_t   *pmr_set_tbl;
+static pmr_tbl_t       *pmr_tbl;
+
+cos_t *get_cos_entry_internal(odp_cos_t cos_id)
 {
-       (void) name;
-       ODP_UNIMPLEMENTED();
+       return &(cos_tbl->cos_entry[cos_id - 1]);
+}
+pmr_set_t *get_pmr_set_entry_internal(odp_pmr_set_t pmr_set_id)
+{
+       return &(pmr_set_tbl->pmr_set[pmr_set_id - 1]);
+}
+
+pmr_t *get_pmr_entry_internal(odp_pmr_t pmr_id)
+{
+       return &(pmr_tbl->pmr[pmr_id - 1]);
+}
+int odp_classification_init_global(void)
+{
+       odp_shm_t cos_shm;
+       odp_shm_t pmr_shm;
+       odp_shm_t pmr_set_shm;
+       int i;
+
+       cos_shm = odp_shm_reserve("odp_cos_pools",
+                       sizeof(cos_tbl_t),
+                       sizeof(cos_t), 0);
+
+       cos_tbl = odp_shm_addr(cos_shm);
+       if (cos_tbl == NULL) {
+               odp_shm_free(cos_shm);
+               return -1;
+       }
+
+       memset(cos_tbl, 0, sizeof(cos_tbl_t));
+       for (i = 0; i < ODP_COS_MAX_ENTRY; i++) {
+               /* init locks */
+               cos_t *cos = get_cos_entry_internal(i + 1);
+               LOCK_INIT(&cos->s.lock);
+       }
+
+       pmr_shm = odp_shm_reserve("odp_pmr_pools",
+                       sizeof(pmr_tbl_t),
+                       sizeof(pmr_t), 0);
+       pmr_tbl = odp_shm_addr(pmr_shm);
+       if (pmr_tbl == NULL) {
+               odp_shm_free(pmr_shm);
+               return -1;
+       }
+
+       memset(pmr_tbl, 0, sizeof(pmr_tbl_t));
+       for (i = 0; i < ODP_PMR_MAX_ENTRY; i++) {
+               /* init locks */
+               pmr_t *pmr = get_pmr_entry_internal(i + 1);
+               LOCK_INIT(&pmr->s.lock);
+       }
+
+       pmr_set_shm = odp_shm_reserve("odp_pmr_set_pools",
+                       sizeof(pmr_set_tbl_t),
+                       sizeof(pmr_set_t), 0);
+       pmr_set_tbl = odp_shm_addr(pmr_set_shm);
+       if (pmr_set_tbl == NULL) {
+               odp_shm_free(pmr_set_shm);
+               return -1;
+       }
+
+       memset(pmr_set_tbl, 0, sizeof(pmr_set_tbl_t));
+       for (i = 0; i < ODP_PMRSET_MAX_ENTRY; i++) {
+               /* init locks */
+               pmr_set_t *pmr = get_pmr_set_entry_internal(i + 1);
+               LOCK_INIT(&pmr->s.pmr.s.lock);
+       }
+
        return 0;
 }
 
+odp_cos_t odp_cos_create(const char *name)
+{
+       int i;
+
+       for (i = 0; i < ODP_COS_MAX_ENTRY; i++) {
+               WRITE_LOCK(&cos_tbl->cos_entry[i].s.lock);
+               if (0 == cos_tbl->cos_entry[i].s.valid) {
+                       strncpy(cos_tbl->cos_entry[i].s.name, name,
+                               ODP_COS_NAME_LEN - 1);
+                       cos_tbl->cos_entry[i].s.name[ODP_COS_NAME_LEN - 1] = 0;
+                       cos_tbl->cos_entry[i].s.pmr = NULL;
+                       cos_tbl->cos_entry[i].s.queue = NULL;
+                       cos_tbl->cos_entry[i].s.pool = NULL;
+                       cos_tbl->cos_entry[i].s.flow_set = 0;
+                       cos_tbl->cos_entry[i].s.headroom = 0;
+                       cos_tbl->cos_entry[i].s.valid = 1;
+                       WRITE_UNLOCK(&cos_tbl->cos_entry[i].s.lock);
+                       return i + 1;
+               }
+               WRITE_UNLOCK(&cos_tbl->cos_entry[i].s.lock);
+       }
+       return ODP_COS_INVALID;
+}
+
+odp_pmr_set_t alloc_pmr_set(pmr_t **pmr)
+{
+       int i;
+
+       for (i = 0; i < ODP_PMRSET_MAX_ENTRY; i++) {
+               WRITE_LOCK(&pmr_set_tbl->pmr_set[i].s.pmr.s.lock);
+               if (0 == pmr_set_tbl->pmr_set[i].s.pmr.s.valid) {
+                       pmr_set_tbl->pmr_set[i].s.pmr.s.valid = 1;
+                       pmr_set_tbl->pmr_set[i].s.pmr.s.num_pmr = 0;
+                       pmr_set_tbl->pmr_set[i].s.pmr.s.cos = NULL;
+                       *pmr = (pmr_t *)&pmr_set_tbl->pmr_set[i];
+                       odp_atomic_init_u32(&pmr_set_tbl->pmr_set[i]
+                                           .s.pmr.s.count);
+                       WRITE_UNLOCK(&pmr_set_tbl->pmr_set[i].s.pmr.s.lock);
+                       return i + 1;
+               }
+               WRITE_UNLOCK(&pmr_set_tbl->pmr_set[i].s.pmr.s.lock);
+       }
+       return ODP_PMR_INVAL;
+}
+
+odp_pmr_t alloc_pmr(pmr_t **pmr)
+{
+       int i;
+
+       for (i = 0; i < ODP_PMR_MAX_ENTRY; i++) {
+               WRITE_LOCK(&pmr_tbl->pmr[i].s.lock);
+               if (0 == pmr_tbl->pmr[i].s.valid) {
+                       pmr_tbl->pmr[i].s.valid = 1;
+                       pmr_tbl->pmr[i].s.cos = NULL;
+                       odp_atomic_init_u32(&pmr_tbl->pmr[i].s.count);
+                       pmr_tbl->pmr[i].s.num_pmr = 0;
+                       *pmr = &pmr_tbl->pmr[i];
+                       WRITE_UNLOCK(&pmr_tbl->pmr[i].s.lock);
+                       return i + 1;
+               }
+               WRITE_UNLOCK(&pmr_tbl->pmr[i].s.lock);
+       }
+       return ODP_PMR_INVAL;
+}
+
+
+cos_t *get_cos_entry(odp_cos_t cos_id)
+{
+       if (cos_id > ODP_COS_MAX_ENTRY || cos_id == ODP_COS_INVALID)
+               return NULL;
+       if (cos_tbl->cos_entry[cos_id - 1].s.valid == 0)
+               return NULL;
+       return &(cos_tbl->cos_entry[cos_id - 1]);
+}
+
+
+pmr_set_t *get_pmr_set_entry(odp_pmr_set_t pmr_set_id)
+{
+       if (pmr_set_id > ODP_PMRSET_MAX_ENTRY || pmr_set_id == ODP_PMR_INVAL)
+               return NULL;
+       if (pmr_set_tbl->pmr_set[pmr_set_id - 1].s.pmr.s.valid == 0)
+               return NULL;
+       return &(pmr_set_tbl->pmr_set[pmr_set_id - 1]);
+}
+
+pmr_t *get_pmr_entry(odp_pmr_t pmr_id)
+{
+       if (pmr_id > ODP_PMR_MAX_ENTRY || pmr_id == ODP_PMR_INVAL)
+               return NULL;
+       if (pmr_tbl->pmr[pmr_id - 1].s.valid == 0)
+               return NULL;
+       return &(pmr_tbl->pmr[pmr_id - 1]);
+}
+
 int odp_cos_destroy(odp_cos_t cos_id)
 {
-       (void)cos_id;
-       ODP_UNIMPLEMENTED();
+       cos_t *cos = get_cos_entry(cos_id);
+       if (NULL == cos)
+               return -1;
+
+       WRITE_LOCK(&cos->s.lock);
+       cos->s.valid = 0;
+       WRITE_UNLOCK(&cos->s.lock);
        return 0;
 }
 
 int odp_cos_set_queue(odp_cos_t cos_id, odp_queue_t queue_id)
 {
-       (void)cos_id;
-       (void)queue_id;
-       ODP_UNIMPLEMENTED();
+       cos_t *cos = get_cos_entry(cos_id);
+       if (cos == NULL)
+               return -1;
+
+       WRITE_LOCK(&cos->s.lock);
+       cos->s.queue = queue_to_qentry(queue_id);
+       WRITE_UNLOCK(&cos->s.lock);
        return 0;
 }
 
 int odp_cos_set_queue_group(odp_cos_t cos_id, odp_queue_group_t queue_group_id)
 {
-       (void)cos_id;
-       (void)queue_group_id;
-       ODP_UNIMPLEMENTED();
+       cos_t *cos = get_cos_entry(cos_id);
+       if (NULL == cos)
+               return -1;
+
+       WRITE_LOCK(&cos->s.lock);
+       cos->s.queue_group = queue_group_id;
+       WRITE_UNLOCK(&cos->s.lock);
        return 0;
 }
 
 int odp_cos_set_pool(odp_cos_t cos_id, odp_buffer_pool_t pool_id)
 {
-       (void)cos_id;
-       (void) pool_id;
-       ODP_UNIMPLEMENTED();
+       pool_entry_t *pool;
+       uint32_t id = pool_handle_to_index(pool_id);
+       cos_t *cos = get_cos_entry(cos_id);
+
+       if (cos == NULL)
+               return -1;
+       pool  = get_pool_entry(id);
+
+       WRITE_LOCK(&cos->s.lock);
+       cos->s.pool = pool;
+       WRITE_UNLOCK(&cos->s.lock);
        return 0;
 }
 
 
 int odp_cos_set_drop(odp_cos_t cos_id, odp_drop_e drop_policy)
 {
-       (void)cos_id;
-       (void)drop_policy;
-       ODP_UNIMPLEMENTED();
+       cos_t *cos = get_cos_entry(cos_id);
+       if (cos == NULL)
+               return -1;
+
+       WRITE_LOCK(&cos->s.lock);
+       cos->s.drop_policy = drop_policy;
+       WRITE_UNLOCK(&cos->s.lock);
        return 0;
 }
 
 int odp_pktio_set_default_cos(odp_pktio_t pktio_in, odp_cos_t default_cos)
 {
-       (void)pktio_in;
-       (void)default_cos;
-       ODP_UNIMPLEMENTED();
+       pktio_entry_t *entry;
+       cos_t *cos;
+       entry = get_pktio_entry(pktio_in);
+       if (entry == NULL)
+               return -1;
+       cos = get_cos_entry(default_cos);
+       if (cos == NULL)
+               return -1;
+
+       WRITE_LOCK(&entry->s.cls.lock);
+       entry->s.cls.default_cos = cos;
+       WRITE_UNLOCK(&entry->s.cls.lock);
+
        return 0;
 }
+
 int odp_pktio_set_error_cos(odp_pktio_t pktio_in, odp_cos_t error_cos)
 {
-       (void)pktio_in;
-       (void)error_cos;
-       ODP_UNIMPLEMENTED();
+       pktio_entry_t *entry;
+       cos_t *cos;
+
+       entry = get_pktio_entry(pktio_in);
+       if (entry == NULL)
+               return -1;
+       cos = get_cos_entry(error_cos);
+       if (cos == NULL)
+               return -1;
+       WRITE_LOCK(&entry->s.cls.lock);
+       entry->s.cls.error_cos = cos;
+       WRITE_UNLOCK(&entry->s.cls.lock);
        return 0;
 }
 
 int odp_pktio_set_skip(odp_pktio_t pktio_in, size_t offset)
 {
-       (void)pktio_in;
-       (void)offset;
-       ODP_UNIMPLEMENTED();
+       pktio_entry_t *entry = get_pktio_entry(pktio_in);
+       if (entry == NULL)
+               return -1;
+       WRITE_LOCK(&entry->s.cls.lock);
+       entry->s.cls.skip = offset;
+       WRITE_UNLOCK(&entry->s.cls.lock);
        return 0;
 }
 
-int odp_pktio_set_headroom(odp_pktio_t port_id, size_t headroom)
+int odp_pktio_set_headroom(odp_pktio_t pktio_in, size_t headroom)
 {
-       (void)port_id;
-       (void)headroom;
-       ODP_UNIMPLEMENTED();
+       pktio_entry_t *entry = get_pktio_entry(pktio_in);
+       if (entry == NULL)
+               return -1;
+       WRITE_LOCK(&entry->s.cls.lock);
+       entry->s.cls.headroom = headroom;
+       WRITE_UNLOCK(&entry->s.cls.lock);
        return 0;
 }
 int odp_cos_set_headroom(odp_cos_t cos_id, size_t req_room)
 {
-       (void)cos_id;
-       (void)req_room;
-       ODP_UNIMPLEMENTED();
+       cos_t *cos = get_cos_entry(cos_id);
+       if (cos == NULL)
+               return -1;
+       WRITE_LOCK(&cos->s.lock);
+       cos->s.headroom = req_room;
+       WRITE_UNLOCK(&cos->s.lock);
        return 0;
 }
 
 int odp_cos_with_l2_priority(odp_pktio_t pktio_in,
-                            size_t num_qos,
-                            uint8_t qos_table[],
-                            odp_cos_t cos_table[])
-{
-       (void)pktio_in;
-       (void)num_qos;
-       (void)qos_table;
-       (void)cos_table;
-       ODP_UNIMPLEMENTED();
+               size_t num_qos,
+               uint8_t qos_table[],
+               odp_cos_t cos_table[])
+{
+       pmr_l2_cos_t *l2_cos;
+       size_t i;
+       cos_t *cos;
+       pktio_entry_t *entry = get_pktio_entry(pktio_in);
+       if (entry == NULL)
+               return -1;
+       READ_LOCK(&entry->s.cls.lock);
+       l2_cos = &entry->s.cls.l2_cos_table;
+       READ_UNLOCK(&entry->s.cls.lock);
+
+       WRITE_LOCK(&l2_cos->lock);
+       /* Update the L2 QoS table*/
+       for (i = 0; i < num_qos; i++) {
+               cos = get_cos_entry(cos_table[i]);
+               if (cos != NULL) {
+                       if (ODP_COS_MAX_L2_QOS > qos_table[i])
+                               l2_cos->cos[qos_table[i]] = cos;
+               }
+       }
+       WRITE_UNLOCK(&l2_cos->lock);
        return 0;
 }
 
 int odp_cos_with_l3_qos(odp_pktio_t pktio_in,
-                       size_t num_qos,
-                       uint8_t qos_table[],
-                       odp_cos_t cos_table[],
-                       bool l3_preference)
-{
-       (void)pktio_in;
-       (void)num_qos;
-       (void)qos_table;
-       (void)cos_table;
-       (void)l3_preference;
-       ODP_UNIMPLEMENTED();
+               size_t num_qos,
+               uint8_t qos_table[],
+               odp_cos_t cos_table[],
+               bool l3_preference)
+{
+       pmr_l3_cos_t *l3_cos;
+       size_t i;
+       pktio_entry_t *entry = get_pktio_entry(pktio_in);
+       cos_t *cos;
+
+       if (entry == NULL)
+               return -1;
+       WRITE_LOCK(&entry->s.cls.lock);
+       entry->s.cls.l3_precedence = l3_preference;
+       l3_cos = &entry->s.cls.l3_cos_table;
+       WRITE_UNLOCK(&entry->s.cls.lock);
+
+       WRITE_LOCK(&l3_cos->lock);
+       /* Update the L3 QoS table*/
+       for (i = 0; i < num_qos; i++) {
+               cos = get_cos_entry(cos_table[i]);
+               if (cos != NULL) {
+                       if (ODP_COS_MAX_L3_QOS > qos_table[i])
+                               l3_cos->cos[qos_table[i]] = cos;
+               }
+       }
+       WRITE_UNLOCK(&l3_cos->lock);
        return 0;
 }
 
-odp_cos_flow_set_t
-odp_cos_class_flow_signature(odp_cos_t cos_id,
-                            odp_cos_flow_set_t req_data_set)
+odp_cos_flow_set_t odp_cos_class_flow_signature(odp_cos_t cos_id,
+               odp_cos_flow_set_t req_data_set)
 {
-       (void)cos_id;
-       (void)req_data_set;
-       ODP_UNIMPLEMENTED();
-       return 0;
+       cos_t *cos = get_cos_entry(cos_id);
+       if (cos == NULL)
+               return -1;
+
+       WRITE_LOCK(&cos->s.lock);
+       cos->s.flow_set = req_data_set;
+       WRITE_UNLOCK(&cos->s.lock);
+       return cos->s.flow_set;
 }
-odp_cos_flow_set_t
-odp_cos_port_flow_signature(odp_pktio_t pktio_in,
-                           odp_cos_flow_set_t req_data_set)
+
+odp_cos_flow_set_t odp_cos_port_flow_signature(odp_pktio_t pktio_in,
+               odp_cos_flow_set_t req_data_set)
 {
-       (void)pktio_in;
-       (void)req_data_set;
-       ODP_UNIMPLEMENTED();
-       return 0;
+       odp_cos_flow_set_t flow_set;
+       pktio_entry_t *entry = get_pktio_entry(pktio_in);
+
+       if (entry == NULL)
+               return -1;
+       WRITE_LOCK(&entry->s.cls.lock);
+       entry->s.cls.flow_set = req_data_set;
+       flow_set = entry->s.cls.flow_set;
+       WRITE_UNLOCK(&entry->s.cls.lock);
+
+       return flow_set;
 }
 
 odp_pmr_t odp_pmr_create_match(odp_pmr_term_e term,
-                              const void *val,
-                              const void *mask,
-                              size_t val_sz)
-{
-       (void)term;
-       (void)val;
-       (void)mask;
-       (void)val_sz;
-       ODP_UNIMPLEMENTED();
-       return 0;
+               const void *val,
+               const void *mask,
+               size_t val_sz)
+{
+       pmr_t *pmr;
+       odp_pmr_t id;
+
+       id = alloc_pmr(&pmr);
+       if (id == ODP_PMR_INVAL)
+               return ODP_PMR_INVAL;
+
+       WRITE_LOCK(&pmr->s.lock);
+       pmr->s.num_pmr = 1;
+       pmr->s.pmr_term_value[0].match_type = ODP_PMR_MASK;
+       pmr->s.pmr_term_value[0].term = term;
+       pmr->s.pmr_term_value[0].mask.val =  0;
+       pmr->s.pmr_term_value[0].mask.mask =  0;
+       memcpy(&pmr->s.pmr_term_value[0].mask.val, val, (val_sz/8) + 1);
+       memcpy(&pmr->s.pmr_term_value[0].mask.mask, mask, (val_sz/8) + 1);
+       WRITE_UNLOCK(&pmr->s.lock);
+       return id;
 }
 
 odp_pmr_t odp_pmr_create_range(odp_pmr_term_e term,
-                              const void *val1,
-                              const void *val2,
-                              size_t val_sz)
-{
-       (void)term;
-       (void)val1;
-       (void)val2;
-       (void)val_sz;
-       ODP_UNIMPLEMENTED();
-       return 0;
+               const void *val1,
+               const void *val2,
+               size_t val_sz)
+{
+       pmr_t *pmr;
+       odp_pmr_t id;
+
+       id = alloc_pmr(&pmr);
+       if (id == ODP_PMR_INVAL)
+               return ODP_PMR_INVAL;
+       WRITE_LOCK(&pmr->s.lock);
+       pmr->s.num_pmr = 1;
+       pmr->s.pmr_term_value[0].match_type = ODP_PMR_MASK;
+       pmr->s.pmr_term_value[0].term = term;
+       pmr->s.pmr_term_value[0].range.val1 =  0;
+       pmr->s.pmr_term_value[0].range.val2 =  0;
+       memcpy(&pmr->s.pmr_term_value[0].range.val1, val1, (val_sz/8) + 1);
+       memcpy(&pmr->s.pmr_term_value[0].range.val2, val2, (val_sz/8) + 1);
+       WRITE_UNLOCK(&pmr->s.lock);
+       return id;
 }
 int odp_pmr_destroy(odp_pmr_t pmr_id)
 {
-       (void)pmr_id;
-       ODP_UNIMPLEMENTED();
+       pmr_t *pmr = get_pmr_entry(pmr_id);
+
+       if (pmr == NULL)
+               return -1;
+       WRITE_LOCK(&pmr->s.lock);
+       pmr->s.valid = 0;
+       WRITE_UNLOCK(&pmr->s.lock);
        return 0;
 }
 
 int odp_pktio_pmr_cos(odp_pmr_t pmr_id,
-                     odp_pktio_t src_pktio,
-                     odp_cos_t dst_cos)
+               odp_pktio_t src_pktio,
+               odp_cos_t dst_cos)
 {
-       (void)pmr_id;
-       (void)src_pktio;
-       (void)dst_cos;
-       ODP_UNIMPLEMENTED();
+       uint8_t num_pmr;
+       pktio_entry_t *pktio_entry;
+       pmr_t *pmr;
+       cos_t *cos;
+
+       pktio_entry = get_pktio_entry(src_pktio);
+       if (pktio_entry == NULL)
+               return -1;
+
+       pmr = get_pmr_entry(pmr_id);
+       if (pmr == NULL)
+               return -1;
+
+       cos = get_cos_entry(dst_cos);
+       if (cos == NULL)
+               return -1;
+
+       num_pmr = pktio_entry->s.cls.num_pmr;
+       if (num_pmr >= ODP_PKTIO_MAX_PMR)
+               return -1;
+
+       WRITE_LOCK(&pktio_entry->s.cls.lock);
+       pktio_entry->s.cls.pmr[num_pmr] = pmr;
+       pktio_entry->s.cls.num_pmr++;
+       WRITE_UNLOCK(&pktio_entry->s.cls.lock);
+
+       WRITE_LOCK(&pmr->s.lock);
+       pmr->s.cos = cos;
+       WRITE_UNLOCK(&pmr->s.lock);
        return 0;
 }
 
 int odp_cos_pmr_cos(odp_pmr_t pmr_id, odp_cos_t src_cos, odp_cos_t dst_cos)
 {
-       (void)pmr_id;
-       (void)src_cos;
-       (void)dst_cos;
-       ODP_UNIMPLEMENTED();
+       cos_t *cos_src = get_cos_entry(src_cos);
+       cos_t *cos_dst = get_cos_entry(dst_cos);
+       pmr_t *pmr = get_pmr_entry(pmr_id);
+       if (NULL == cos_src || NULL == cos_dst || NULL == pmr)
+               return -1;
+
+       WRITE_LOCK(&cos_src->s.lock);
+       cos_src->s.pmr = pmr;
+       WRITE_UNLOCK(&cos_src->s.lock);
+
+       WRITE_LOCK(&pmr->s.lock);
+       pmr->s.cos = cos_dst;
+       WRITE_UNLOCK(&pmr->s.lock);
        return 0;
 }
 
 signed long odp_pmr_match_count(odp_pmr_t pmr_id)
 {
-       (void)pmr_id;
-       ODP_UNIMPLEMENTED();
-       return 0;
+       pmr_t *pmr = get_pmr_entry(pmr_id);
+       if (pmr == NULL)
+               return -1;
+       return pmr->s.count;
 }
 
 unsigned long long odp_pmr_terms_cap(void)
 {
-       ODP_UNIMPLEMENTED();
-       return 0;
+       unsigned long long term_cap = 0;
+
+       term_cap |= (1 << ODP_PMR_LEN);
+       term_cap |= (1 << ODP_PMR_IPPROTO);
+       term_cap |= (1 << ODP_PMR_UDP_DPORT);
+       term_cap |= (1 << ODP_PMR_TCP_DPORT);
+       term_cap |= (1 << ODP_PMR_UDP_SPORT);
+       term_cap |= (1 << ODP_PMR_TCP_SPORT);
+       term_cap |= (1 << ODP_PMR_SIP_ADDR);
+       term_cap |= (1 << ODP_PMR_DIP_ADDR);
+       return term_cap;
 }
 
 unsigned odp_pmr_terms_avail(void)
 {
-       ODP_UNIMPLEMENTED();
-       return 0;
+       unsigned count = 0;
+       int i;
+
+       for (i = 0; i < ODP_PMR_MAX_ENTRY; i++)
+               if (!pmr_tbl->pmr[i].s.valid)
+                       count++;
+       return count;
 }
 
 int odp_pmr_match_set_create(int num_terms, odp_pmr_match_t *terms,
-                            odp_cos_t dst_cos, odp_pmr_set_t *pmr_set_id)
+                            odp_pmr_set_t *pmr_set_id)
 {
-       (void)num_terms;
-       (void)terms;
-       (void)pmr_set_id;
-       (void)dst_cos;
-       ODP_UNIMPLEMENTED();
-       return 0;
+       pmr_t *pmr;
+       int i;
+       uint32_t id;
+       int val_sz;
+       int num;
+       int count = 0;
+
+       id = alloc_pmr_set(&pmr);
+       if (id == ODP_PMR_INVAL) {
+               *pmr_set_id = id;
+               return -1;
+       }
+
+       WRITE_LOCK(&pmr->s.lock);
+       pmr->s.num_pmr = num_terms;
+       for (i = 0; i < num_terms; i++) {
+               pmr->s.pmr_term_value[i].match_type = terms[i].match_type;
+               if (terms[i].match_type == ODP_PMR_MASK) {
+                       num = terms[i].mask.val_sz;
+                       if (num > ODP_PMR_TERM_BITS_MAX)
+                               continue;
+                       val_sz = (num/8) + (num%8) ? 1 : 0;
+                       pmr->s.pmr_term_value[i].term = terms[i].mask.term;
+                       pmr->s.pmr_term_value[i].mask.val = 0;
+                       pmr->s.pmr_term_value[i].mask.mask = 0;
+                       memcpy(&pmr->s.pmr_term_value[i].mask.val,
+                              terms[i].mask.val, val_sz);
+                       memcpy(&pmr->s.pmr_term_value[i].mask.mask,
+                              terms[i].mask.mask, val_sz);
+               } else {
+                       num = terms[i].range.val_sz;
+                       if (num > ODP_PMR_TERM_BITS_MAX)
+                               continue;
+                       val_sz = (num/8) + (num%8) ? 1 : 0;
+                       pmr->s.pmr_term_value[i].term = terms[i].range.term;
+                       pmr->s.pmr_term_value[i].range.val1 = 0;
+                       pmr->s.pmr_term_value[i].range.val2 = 0;
+                       memcpy(&pmr->s.pmr_term_value[i].range.val1,
+                              terms[i].range.val1, val_sz);
+                       memcpy(&pmr->s.pmr_term_value[i].range.val2,
+                              terms[i].range.val2, val_sz);
+               }
+               count++;
+       }
+       WRITE_UNLOCK(&pmr->s.lock);
+       *pmr_set_id = id;
+       return count;
 }
 
 int odp_pmr_match_set_destroy(odp_pmr_set_t pmr_set_id)
 {
-       (void)pmr_set_id;
-       ODP_UNIMPLEMENTED();
+       pmr_set_t *pmr_set = get_pmr_set_entry(pmr_set_id);
+       if (pmr_set == NULL)
+               return -1;
+       WRITE_LOCK(&pmr_set->s.pmr.s.lock);
+       pmr_set->s.pmr.s.valid = 0;
+       WRITE_UNLOCK(&pmr_set->s.pmr.s.lock);
        return 0;
 }
 
 int odp_pktio_pmr_match_set_cos(odp_pmr_set_t pmr_set_id, odp_pktio_t 
src_pktio,
-                               odp_cos_t dst_cos)
+               odp_cos_t dst_cos)
+{
+       uint8_t num_pmr;
+       pktio_entry_t *pktio_entry;
+       pmr_t *pmr;
+       cos_t *cos;
+
+       pktio_entry = get_pktio_entry(src_pktio);
+       if (pktio_entry == NULL)
+               return -1;
+
+       pmr = (pmr_t *)get_pmr_set_entry(pmr_set_id);
+       if (pmr == NULL)
+               return -1;
+
+       cos = get_cos_entry(dst_cos);
+       if (cos == NULL)
+               return -1;
+
+       num_pmr = pktio_entry->s.cls.num_pmr;
+       if (num_pmr >= ODP_PKTIO_MAX_PMR)
+               return -1;
+
+       WRITE_LOCK(&pktio_entry->s.cls.lock);
+       pktio_entry->s.cls.pmr[num_pmr] = pmr;
+       pktio_entry->s.cls.num_pmr++;
+       WRITE_UNLOCK(&pktio_entry->s.cls.lock);
+
+       WRITE_LOCK(&pmr->s.lock);
+       pmr->s.cos = cos;
+       WRITE_UNLOCK(&pmr->s.lock);
+       return 0;
+}
+
+int verify_pmr(pmr_t *pmr, uint8_t *pkt_addr, odp_packet_hdr_t *pkt_hdr)
+{
+       int pmr_failure = 0;
+       int num_pmr;
+       int i;
+       pmr_term_value_t *term_value;
+
+       READ_LOCK(&pmr->s.lock);
+       if (!pmr->s.valid) {
+               READ_UNLOCK(&pmr->s.lock);
+               return 0;
+       }
+       num_pmr = pmr->s.num_pmr;
+
+       /* Iterate through list of PMR Term values in a pmr_t */
+       for (i = 0; i < num_pmr; i++) {
+               term_value = &pmr->s.pmr_term_value[i];
+               switch (term_value->term) {
+               case ODP_PMR_LEN:
+                       if (!verify_pmr_packet_len(pkt_hdr, term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_ETHTYPE_0:
+                       if (!verify_pmr_eth_type_0(pkt_addr, pkt_hdr,
+                                                  term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_ETHTYPE_X:
+                       if (!verify_pmr_eth_type_x(pkt_addr, pkt_hdr,
+                                                  term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_VLAN_ID_0:
+                       if (!verify_pmr_vlan_id_0(pkt_addr, pkt_hdr,
+                                                 term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_VLAN_ID_X:
+                       if (!verify_pmr_vlan_id_x(pkt_addr, pkt_hdr,
+                                                 term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_DMAC:
+                       if (!verify_pmr_dmac(pkt_addr, pkt_hdr,
+                                            term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_IPPROTO:
+                       if (!verify_pmr_ip_proto(pkt_addr, pkt_hdr,
+                                                term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_UDP_DPORT:
+                       if (!verify_pmr_udp_dport(pkt_addr, pkt_hdr,
+                                                 term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_TCP_DPORT:
+                       if (!verify_pmr_tcp_dport(pkt_addr, pkt_hdr,
+                                                 term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_UDP_SPORT:
+                       if (!verify_pmr_udp_sport(pkt_addr, pkt_hdr,
+                                                 term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_TCP_SPORT:
+                       if (!verify_pmr_tcp_sport(pkt_addr, pkt_hdr,
+                                                 term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_SIP_ADDR:
+                       if (!verify_pmr_ipv4_saddr(pkt_addr, pkt_hdr,
+                                                  term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_DIP_ADDR:
+                       if (!verify_pmr_ipv4_daddr(pkt_addr, pkt_hdr,
+                                                  term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_SIP6_ADDR:
+                       if (!verify_pmr_ipv6_saddr(pkt_addr, pkt_hdr,
+                                                  term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_DIP6_ADDR:
+                       if (!verify_pmr_ipv6_daddr(pkt_addr, pkt_hdr,
+                                                  term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_IPSEC_SPI:
+                       if (!verify_pmr_ipsec_spi(pkt_addr, pkt_hdr,
+                                                 term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_LD_VNI:
+                       if (!verify_pmr_ld_vni(pkt_addr, pkt_hdr,
+                                              term_value))
+                               pmr_failure = 1;
+                       break;
+               case ODP_PMR_INNER_HDR_OFF:
+                       break;
+       }
+       if (pmr_failure) {
+               READ_UNLOCK(&pmr->s.lock);
+               return false;
+       }
+       }
+       READ_UNLOCK(&pmr->s.lock);
+       odp_atomic_fetch_add_u32(&pmr->s.count, 1);
+       return true;
+}
+
+cos_t *match_pmr_cos(cos_t *cos, uint8_t *pkt_addr, pmr_t *pmr,
+                    odp_packet_hdr_t *hdr)
+{
+       cos_t *retcos = NULL;
+
+       if (cos == NULL || pmr == NULL)
+               return NULL;
+
+       READ_LOCK(&cos->s.lock);
+       if (NULL == cos->s.pmr) {
+               READ_UNLOCK(&cos->s.lock);
+               return cos;
+       }
+
+       if (!cos->s.valid) {
+               READ_UNLOCK(&cos->s.lock);
+               return NULL;
+       }
+       READ_UNLOCK(&cos->s.lock);
+
+       if (verify_pmr(pmr, pkt_addr, hdr)) {
+               /** This gets called recursively to check all the PMRs in
+                * a PMR chain */
+               retcos = match_pmr_cos(pmr->s.cos, pkt_addr, cos->s.pmr, hdr);
+               if (!retcos)
+                       return cos;
+       }
+       return retcos;
+}
+
+int packet_classifier(odp_pktio_t pktio, odp_packet_t pkt, size_t len,
+                     size_t frame_offset)
+{
+       pktio_entry_t *entry;
+       queue_entry_t *queue;
+       cos_t *cos;
+       odp_packet_hdr_t *pkt_hdr;
+       uint8_t *pkt_addr;
+
+       entry = get_pktio_entry(pktio);
+       if (entry == NULL)
+               return -1;
+       /* Packet Parsing routine */
+       odp_packet_parse(pkt, len, frame_offset);
+
+       pkt_hdr = odp_packet_hdr(pkt);
+       pkt_addr = odp_packet_data(pkt);
+
+       /* Matching PMR and selecting the CoS for the packet*/
+       cos = pktio_select_cos(entry, pkt_addr, pkt_hdr);
+       if (cos == NULL)
+               return -1;
+
+       /*Updating the packet Flow signature meta */
+       update_flow_signature(pkt_addr, cos);
+
+       /* Enqueuing the Packet based on the CoS */
+       READ_LOCK(&cos->s.lock);
+       queue = cos->s.queue;
+       READ_UNLOCK(&cos->s.lock);
+       return queue_enq(queue, odp_buf_to_hdr((odp_buffer_t)pkt));
+}
+
+cos_t *pktio_select_cos(pktio_entry_t *entry, uint8_t *pkt_addr,
+                      odp_packet_hdr_t *pkt_hdr)
+{
+       pmr_t *pmr;
+       cos_t *cos = NULL;
+       int i;
+       classifier_t *cls = &entry->s.cls;
+
+       /* Return error cos for error packet */
+       if (pkt_hdr->error_flags.all)
+               return cls->error_cos;
+       /* Calls all the PMRs attached at the PKTIO level*/
+       for (i = 0; i < cls->num_pmr; i++) {
+               pmr = entry->s.cls.pmr[i];
+               if (pmr) {
+                       cos = match_pmr_cos(pmr->s.cos, pkt_addr, pmr, pkt_hdr);
+                       if (cos)
+                               return cos;
+               }
+       }
+
+       cos = match_qos_cos(entry, pkt_addr, pkt_hdr);
+       if (cos)
+               return cos;
+
+       return cls->default_cos;
+}
+
+cos_t *match_qos_l3_cos(pmr_l3_cos_t *l3_cos, uint8_t *pkt_addr,
+                       odp_packet_hdr_t *hdr)
+{
+       uint8_t dscp;
+       cos_t *cos = NULL;
+       odph_ipv4hdr_t *ipv4;
+       odph_ipv6hdr_t *ipv6;
+
+       READ_LOCK(&l3_cos->lock);
+       if (hdr->input_flags.l3 && hdr->input_flags.ipv4) {
+               ipv4 = (odph_ipv4hdr_t *)(pkt_addr + hdr->l3_offset);
+               dscp = ODPH_IPV4HDR_DSCP(ipv4->tos);
+               cos = l3_cos->cos[dscp];
+       } else if (hdr->input_flags.l3 && hdr->input_flags.ipv6) {
+               ipv6 = (odph_ipv6hdr_t *)(pkt_addr + hdr->l3_offset);
+               dscp = ODPH_IPV6HDR_DSCP(ipv6->ver_tc_flow);
+               cos = l3_cos->cos[dscp];
+       }
+       READ_UNLOCK(&l3_cos->lock);
+
+       return cos;
+}
+
+cos_t *match_qos_l2_cos(pmr_l2_cos_t *l2_cos, uint8_t *pkt_addr,
+                       odp_packet_hdr_t *hdr)
+{
+       uint8_t qos;
+       cos_t *cos = NULL;
+       odph_ethhdr_t *eth;
+       odph_vlanhdr_t *vlan;
+
+       if (hdr->input_flags.l2 && hdr->input_flags.vlan &&
+           hdr->input_flags.eth) {
+               eth = (odph_ethhdr_t *)(pkt_addr + hdr->l2_offset);
+               vlan = (odph_vlanhdr_t *)(&eth->type);
+               qos = ((vlan->tci >> 13) & 0xFF);
+               READ_LOCK(&l2_cos->lock);
+               cos = l2_cos->cos[qos];
+               READ_UNLOCK(&l2_cos->lock);
+       }
+       return cos;
+}
+
+cos_t *match_qos_cos(pktio_entry_t *entry, uint8_t *pkt_addr,
+                    odp_packet_hdr_t *hdr)
+{
+       classifier_t *cls = &entry->s.cls;
+       pmr_l2_cos_t *l2_cos;
+       pmr_l3_cos_t *l3_cos;
+       cos_t *cos;
+
+       READ_LOCK(&cls->lock);
+       l2_cos = &cls->l2_cos_table;
+       l3_cos = &cls->l3_cos_table;
+       READ_UNLOCK(&cls->lock);
+
+       if (cls->l3_precedence) {
+               cos =  match_qos_l3_cos(l3_cos, pkt_addr, hdr);
+               if (cos)
+                       return cos;
+               cos = match_qos_l2_cos(l2_cos, pkt_addr, hdr);
+               if (cos)
+                       return cos;
+       } else {
+               cos = match_qos_l2_cos(l2_cos, pkt_addr, hdr);
+               if (cos)
+                       return cos;
+               cos = match_qos_l3_cos(l3_cos, pkt_addr, hdr);
+               if (cos)
+                       return cos;
+       }
+       return NULL;
+}
+
+int update_flow_signature(uint8_t *pkt_addr, cos_t *cos)
 {
-       (void)pmr_set_id;
-       (void)src_pktio;
-       (void)dst_cos;
+       (void)pkt_addr;
+       (void)cos;
        ODP_UNIMPLEMENTED();
        return 0;
 }
-- 
2.0.1.472.g6f92e5f


_______________________________________________
lng-odp mailing list
[email protected]
http://lists.linaro.org/mailman/listinfo/lng-odp

Reply via email to