qos: Store function 53/20953/3
authorNeale Ranns <nranns@cisco.com>
Wed, 31 Jul 2019 09:48:02 +0000 (02:48 -0700)
committerNeale Ranns <nranns@cisco.com>
Wed, 31 Jul 2019 16:17:36 +0000 (16:17 +0000)
Type: feature

store: write a QoS value into the buffer meta-data
record: Extract a QoS value from a packet header and store it.
mark: Make a change to the content of a packet header by writing a stored
      QoS value

Change-Id: I07d1e87dd1ca90d40ac1ae1774fee1b272cab83f
Signed-off-by: Neale Ranns <nranns@cisco.com>
16 files changed:
extras/vom/vom/CMakeLists.txt
extras/vom/vom/qos_map.hpp
extras/vom/vom/qos_store.cpp [new file with mode: 0644]
extras/vom/vom/qos_store.hpp [new file with mode: 0644]
extras/vom/vom/qos_store_cmds.cpp [new file with mode: 0644]
extras/vom/vom/qos_store_cmds.hpp [new file with mode: 0644]
extras/vom/vom/qos_types.hpp
src/vnet/CMakeLists.txt
src/vnet/qos/qos.api
src/vnet/qos/qos_api.c
src/vnet/qos/qos_store.c [new file with mode: 0644]
src/vnet/qos/qos_store.h [new file with mode: 0644]
src/vnet/qos/qos_store_node.c [new file with mode: 0644]
test/ext/vom_test.cpp
test/test_qos.py
test/vpp_qos.py

index 2ea84d9..ad5a66e 100644 (file)
@@ -173,6 +173,8 @@ list(APPEND VOM_SOURCES
   qos_mark_cmds.cpp
   qos_record.cpp
   qos_record_cmds.cpp
+  qos_store.cpp
+  qos_store_cmds.cpp
   qos_types.cpp
   qos_types_api.cpp
   ra_config.cpp
@@ -280,6 +282,7 @@ list(APPEND VOM_HEADERS
   qos_map.hpp
   qos_mark.hpp
   qos_record.hpp
+  qos_store.hpp
   qos_types.hpp
   ra_config.hpp
   ra_prefix.hpp
index d722004..8d235c4 100644 (file)
@@ -35,7 +35,7 @@ namespace QoS {
 class map : public object_base
 {
 public:
-  typedef std::array<std::array<uint8_t, 256>, 4> outputs_t;
+  typedef std::array<std::array<bits_t, 256>, 4> outputs_t;
 
   map(uint32_t id, const outputs_t& o);
   map(const map& r);
diff --git a/extras/vom/vom/qos_store.cpp b/extras/vom/vom/qos_store.cpp
new file mode 100644 (file)
index 0000000..08c2586
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2019 Cisco and/or its affiliates.
+ * 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.
+ */
+
+#include "vom/qos_store.hpp"
+#include "vom/api_types.hpp"
+#include "vom/qos_store_cmds.hpp"
+#include "vom/qos_types_api.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+namespace QoS {
+
+singular_db<store::key_t, store> store::m_db;
+
+store::event_handler store::m_evh;
+
+store::store(const interface& itf, const source_t& src, bits_t value)
+  : m_config(false)
+  , m_itf(itf.singular())
+  , m_src(src)
+  , m_value(value)
+{
+}
+
+store::store(const store& s)
+  : m_config(s.m_config)
+  , m_itf(s.m_itf)
+  , m_src(s.m_src)
+  , m_value(s.m_value)
+{
+}
+
+store::~store()
+{
+  sweep();
+  m_db.release(key(), this);
+}
+
+const store::key_t
+store::key() const
+{
+  return (std::make_pair(m_itf->key(), m_src));
+}
+
+bool
+store::operator==(const store& r) const
+{
+  return (key() == r.key());
+}
+
+void
+store::sweep()
+{
+  if (m_config) {
+    HW::enqueue(new store_cmds::delete_cmd(m_config, m_itf->handle(), m_src));
+  }
+  HW::write();
+}
+
+void
+store::replay()
+{
+  if (m_config) {
+    HW::enqueue(
+      new store_cmds::create_cmd(m_config, m_itf->handle(), m_src, m_value));
+  }
+}
+
+std::string
+store::to_string() const
+{
+  std::ostringstream s;
+  s << "qos-store:[" << m_itf->to_string() << ", src:" << m_src.to_string()
+    << ", value:" << static_cast<int>(m_value);
+
+  return (s.str());
+}
+
+void
+store::update(const store& r)
+{
+  if (rc_t::OK != m_config.rc()) {
+    HW::enqueue(
+      new store_cmds::create_cmd(m_config, m_itf->handle(), m_src, m_value));
+  }
+}
+
+std::shared_ptr<store>
+store::find_or_add(const store& temp)
+{
+  return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<store>
+store::find(const key_t& k)
+{
+  return (m_db.find(k));
+}
+
+std::shared_ptr<store>
+store::singular() const
+{
+  return find_or_add(*this);
+}
+
+void
+store::dump(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+store::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "qos-store" }, "QoS Store", this);
+}
+
+void
+store::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+void
+store::event_handler::handle_populate(const client_db::key_t& key)
+{
+  std::shared_ptr<store_cmds::dump_cmd> cmd =
+    std::make_shared<store_cmds::dump_cmd>();
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& rr : *cmd) {
+    auto& payload = rr.get_payload();
+
+    std::shared_ptr<interface> itf = interface::find(payload.store.sw_if_index);
+
+    VOM_LOG(log_level_t::DEBUG) << "data: " << payload.store.sw_if_index;
+
+    if (itf) {
+      store qr(*itf, from_api(payload.store.input_source), payload.store.value);
+      OM::commit(key, qr);
+
+      VOM_LOG(log_level_t::DEBUG) << "read: " << qr.to_string();
+    } else {
+      VOM_LOG(log_level_t::ERROR) << "no interface:"
+                                  << payload.store.sw_if_index;
+    }
+  }
+}
+
+dependency_t
+store::event_handler::order() const
+{
+  return (dependency_t::ENTRY);
+}
+
+void
+store::event_handler::show(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+}; // namespace QoS
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/qos_store.hpp b/extras/vom/vom/qos_store.hpp
new file mode 100644 (file)
index 0000000..b688314
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2019 Cisco and/or its affiliates.
+ * 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.
+ */
+
+#ifndef __VOM_QOS_STORE_H__
+#define __VOM_QOS_STORE_H__
+
+#include <ostream>
+
+#include "vom/interface.hpp"
+#include "vom/qos_types.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * Types belonging to QoS
+ */
+namespace QoS {
+
+class store : public object_base
+{
+public:
+  store(const interface& i, const source_t& source, bits_t value);
+  store(const store& r);
+
+  ~store();
+
+  typedef std::pair<interface::key_t, source_t> key_t;
+
+  /**
+   * Return the object's key
+   */
+  const key_t key() const;
+
+  /**
+   * comparison operator
+   */
+  bool operator==(const store& bdae) const;
+
+  /**
+   * Return the matching 'singular instance'
+   */
+  std::shared_ptr<store> singular() const;
+
+  /**
+   * Find the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<store> find(const key_t& k);
+
+  /**
+   * Dump all bridge_domain-doamin into the stream provided
+   */
+  static void dump(std::ostream& os);
+
+  /**
+   * replay the object to create it in hardware
+   */
+  void replay(void);
+
+  /**
+   * Convert to string for debugging
+   */
+  std::string to_string() const;
+
+private:
+  /**
+   * Class definition for listeners to OM events
+   */
+  class event_handler : public OM::listener, public inspect::command_handler
+  {
+  public:
+    event_handler();
+    virtual ~event_handler() = default;
+
+    /**
+     * Handle a populate event
+     */
+    void handle_populate(const client_db::key_t& key);
+
+    /**
+     * Handle a replay event
+     */
+    void handle_replay();
+
+    /**
+     * Show the object in the Singular DB
+     */
+    void show(std::ostream& os);
+
+    /**
+     * Get the sortable Id of the listener
+     */
+    dependency_t order() const;
+  };
+
+  /**
+   * event_handler to register with OM
+   */
+  static event_handler m_evh;
+
+  /**
+   * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
+   */
+  void update(const store& obj);
+
+  /**
+   * Find or add the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<store> find_or_add(const store& temp);
+
+  /*
+   * It's the VPPHW class that updates the objects in HW
+   */
+  friend class VOM::OM;
+
+  /**
+   * It's the singular_db class that calls replay()
+   */
+  friend class singular_db<key_t, store>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the config. The bool representing the
+   * do/don't configured/unconfigured.
+   */
+  HW::item<bool> m_config;
+
+  /**
+   * The interface the endpoint is attached to.
+   */
+  std::shared_ptr<interface> m_itf;
+
+  /**
+   * QoS source to store from
+   */
+  source_t m_src;
+
+  /**
+   * QoS Value to store in the buffer
+   */
+  bits_t m_value;
+
+  /**
+   * A map of all bridge_domains
+   */
+  static singular_db<key_t, store> m_db;
+};
+
+}; // namesapce QoS
+
+std::ostream& operator<<(std::ostream& os, const QoS::store::key_t& key);
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/qos_store_cmds.cpp b/extras/vom/vom/qos_store_cmds.cpp
new file mode 100644 (file)
index 0000000..2718c7a
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2019 Cisco and/or its affiliates.
+ * 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.
+ */
+
+#include "vom/qos_store_cmds.hpp"
+#include "vom/qos_types_api.hpp"
+
+namespace VOM {
+namespace QoS {
+namespace store_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+                       const handle_t& itf,
+                       const source_t& s,
+                       bits_t value)
+  : rpc_cmd(item)
+  , m_itf(itf)
+  , m_src(s)
+  , m_value(value)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+  return ((m_itf == other.m_itf) && (m_src == other.m_src) &&
+          (m_value == other.m_value));
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.store.sw_if_index = m_itf.value();
+  payload.store.input_source = to_api(m_src);
+  payload.store.value = m_value;
+
+  VAPI_CALL(req.execute());
+
+  return (wait());
+}
+
+std::string
+create_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "qos-store-create: " << m_hw_item.to_string() << " itf:" << m_itf
+    << " src:" << m_src.to_string() << " value:" << static_cast<int>(m_value);
+
+  return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item,
+                       const handle_t& itf,
+                       const source_t& s)
+  : rpc_cmd(item)
+  , m_itf(itf)
+  , m_src(s)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+  return (m_hw_item == other.m_hw_item && m_itf == other.m_itf &&
+          m_src == other.m_src);
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.store.sw_if_index = m_itf.value();
+  payload.store.input_source = to_api(m_src);
+
+  VAPI_CALL(req.execute());
+
+  return (wait());
+}
+
+std::string
+delete_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "qos-store-delete: " << m_hw_item.to_string();
+
+  return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+  return (true);
+}
+
+rc_t
+dump_cmd::issue(connection& con)
+{
+  m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+  VAPI_CALL(m_dump->execute());
+
+  wait();
+
+  return rc_t::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+  return ("qos-store-dump");
+}
+
+}; // namespace store_cmds
+}; // namespace QoS
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/qos_store_cmds.hpp b/extras/vom/vom/qos_store_cmds.hpp
new file mode 100644 (file)
index 0000000..3567108
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2019 Cisco and/or its affiliates.
+ * 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.
+ */
+
+#ifndef __VOM_QOS_STORE_CMDS_H__
+#define __VOM_QOS_STORE_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/qos_store.hpp"
+
+#include <vapi/qos.api.vapi.hpp>
+
+namespace VOM {
+namespace QoS {
+namespace store_cmds {
+
+/**
+ * A command class that creates or updates the GBP endpoint
+ */
+class create_cmd
+  : public rpc_cmd<HW::item<bool>, vapi::Qos_store_enable_disable>
+{
+public:
+  /**
+   * Constructor
+   */
+  create_cmd(HW::item<bool>& item,
+             const handle_t& itf,
+             const source_t& src,
+             bits_t value);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const create_cmd& i) const;
+
+private:
+  handle_t m_itf;
+  const source_t& m_src;
+  bits_t m_value;
+};
+
+/**
+ * A cmd class that deletes a GBP endpoint
+ */
+class delete_cmd
+  : public rpc_cmd<HW::item<bool>, vapi::Qos_store_enable_disable>
+{
+public:
+  /**
+   * Constructor
+   */
+  delete_cmd(HW::item<bool>& item, const handle_t& itf, const source_t& src);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const delete_cmd& i) const;
+
+private:
+  const handle_t m_itf;
+  const source_t& m_src;
+};
+
+/**
+ * A cmd class that Dumps all the GBP endpoints
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Qos_store_dump>
+{
+public:
+  /**
+   * Constructor
+   */
+  dump_cmd();
+  dump_cmd(const dump_cmd& d);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const dump_cmd& i) const;
+
+private:
+  /**
+   * HW reutrn code
+   */
+  HW::item<bool> item;
+};
+}; // namespace store_cmds
+}; // namespace Qos
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
index fea3780..e3c6a16 100644 (file)
@@ -24,6 +24,8 @@ namespace VOM {
  */
 namespace QoS {
 
+typedef uint8_t bits_t;
+
 /**
  * The Source of the QoS classification (i.e. which header the bits are
  * associated with).
index aaf7357..05a8077 100644 (file)
@@ -1451,6 +1451,8 @@ list(APPEND VNET_SOURCES
   qos/qos_egress_map.c
   qos/qos_record.c
   qos/qos_record_node.c
+  qos/qos_store.c
+  qos/qos_store_node.c
   qos/qos_mark.c
   qos/qos_mark_node.c
 )
index 11c9163..dcd9ce5 100644 (file)
  * limitations under the License.
  */
 
-/** \file
-
-    This file defines QoS record and mark API messages which are generally
-    called through a shared memory interface.
-*/
+/**
+ * @file
+ * This file defines QoS record, store and mark API messages which are generally
+ *    called through a shared memory interface.
+ *
+ * Definitions of verbs:
+ *  store: write a QoS value into the buffer meta-data
+ *  record: Extract a QoS value from a packet header and store it.
+ *  mark: Make a change to the content of a packet header by writing a stored
+ *        QoS value
+ */
+option version = "1.1.0";
 
-option version = "1.0.0";
+import "vnet/ip/ip_types.api";
 
 enum qos_source : u8
 {
@@ -30,6 +37,55 @@ enum qos_source : u8
   QOS_API_SOURCE_IP = 3,
 };
 
+/**
+ * QoS store.
+ * @param sw_if_index - The interface on which store is enabled.
+ * @param input_source - The input source/layer at which the QoS bits are stored
+ *                       See qos_source_t. This determines what packets have a
+ *                       value stored. i.e. If IP is seleted this affects on IP
+ *                       packets.
+ * @param value - The value to store.
+ */
+typedef qos_store
+{
+  u32 sw_if_index;
+  vl_api_qos_source_t input_source;
+  u8 value;
+};
+
+/**
+ * Enable/Disable QoS storing
+ * The QoS bits from the packet at the specified input layer are copied
+ * into the packet. Storeing should be used in conjunction with marking
+ * @param enable - enable=1 or disable the feature
+ * @param store - Store configuration
+ */
+autoreply define qos_store_enable_disable
+{
+  u32 client_index;
+  u32 context;
+  u8 enable;
+  vl_api_qos_store_t store;
+};
+
+/**
+ * Dump the QoS store configs
+ */
+define qos_store_dump
+{
+  u32 client_index;
+  u32 context;
+};
+
+/**
+ * Details of QoS recording configs
+ */
+define qos_store_details
+{
+  u32 context;
+  vl_api_qos_store_t store;
+};
+
 /**
  * QoS recording.
  * @param sw_if_index - The interface on which recording is enabled.
index 966ffcc..cb92c85 100644 (file)
@@ -20,6 +20,7 @@
 #include <vnet/api_errno.h>
 
 #include <vnet/qos/qos_record.h>
+#include <vnet/qos/qos_store.h>
 #include <vnet/qos/qos_mark.h>
 #include <vnet/qos/qos_egress_map.h>
 
@@ -45,6 +46,8 @@
 #define foreach_qos_api_msg                                             \
   _(QOS_RECORD_ENABLE_DISABLE, qos_record_enable_disable)               \
   _(QOS_RECORD_DUMP, qos_record_dump)                                   \
+  _(QOS_STORE_ENABLE_DISABLE, qos_store_enable_disable)                 \
+  _(QOS_STORE_DUMP, qos_store_dump)                                     \
   _(QOS_EGRESS_MAP_DELETE, qos_egress_map_delete)                       \
   _(QOS_EGRESS_MAP_UPDATE, qos_egress_map_update)                       \
   _(QOS_EGRESS_MAP_DUMP, qos_egress_map_dump)                           \
@@ -144,6 +147,74 @@ vl_api_qos_record_dump_t_handler (vl_api_qos_record_dump_t * mp)
   qos_record_walk (send_qos_record_details, &ctx);
 }
 
+void
+vl_api_qos_store_enable_disable_t_handler (vl_api_qos_store_enable_disable_t
+                                          * mp)
+{
+  vl_api_qos_store_enable_disable_reply_t *rmp;
+  qos_source_t qs;
+  int rv = 0;
+
+  VALIDATE_SW_IF_INDEX (&(mp->store));
+
+  rv = qos_source_decode (mp->store.input_source, &qs);
+
+  if (0 == rv)
+    {
+      if (mp->enable)
+       rv = qos_store_enable (ntohl (mp->store.sw_if_index), qs,
+                              mp->store.value);
+      else
+       rv = qos_store_disable (ntohl (mp->store.sw_if_index), qs);
+    }
+
+  BAD_SW_IF_INDEX_LABEL;
+  REPLY_MACRO (VL_API_QOS_STORE_ENABLE_DISABLE_REPLY);
+}
+
+typedef struct qos_store_send_walk_ctx_t_
+{
+  vl_api_registration_t *reg;
+  u32 context;
+} qos_store_send_walk_ctx_t;
+
+static walk_rc_t
+send_qos_store_details (u32 sw_if_index,
+                       qos_source_t input_source, qos_bits_t value, void *c)
+{
+  qos_store_send_walk_ctx_t *ctx;
+  vl_api_qos_store_details_t *mp;
+
+  ctx = c;
+  mp = vl_msg_api_alloc_zero (sizeof (*mp));
+
+  mp->_vl_msg_id = ntohs (VL_API_QOS_STORE_DETAILS);
+  mp->context = ctx->context;
+  mp->store.sw_if_index = htonl (sw_if_index);
+  mp->store.input_source = qos_source_encode (input_source);
+  mp->store.value = value;
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (WALK_CONTINUE);
+}
+
+static void
+vl_api_qos_store_dump_t_handler (vl_api_qos_store_dump_t * mp)
+{
+  vl_api_registration_t *reg;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  qos_store_send_walk_ctx_t ctx = {
+    .reg = reg,
+    .context = mp->context,
+  };
+  qos_store_walk (send_qos_store_details, &ctx);
+}
+
 void
 vl_api_qos_egress_map_update_t_handler (vl_api_qos_egress_map_update_t * mp)
 {
diff --git a/src/vnet/qos/qos_store.c b/src/vnet/qos/qos_store.c
new file mode 100644 (file)
index 0000000..18e79a3
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * 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.
+ */
+
+#include <vnet/qos/qos_store.h>
+#include <vnet/ip/ip.h>
+#include <vnet/feature/feature.h>
+
+/**
+ * QoS Store configuration
+ */
+typedef struct qos_store_t_
+{
+  u8 qst_n_cfgs;
+  qos_bits_t qst_value;
+} qos_store_t;
+
+/**
+ * Per-interface, per-protocol vector of feature on/off configurations
+ */
+qos_store_t *qos_store_configs[QOS_N_SOURCES];
+
+static void
+qos_store_feature_config (u32 sw_if_index,
+                         qos_source_t input_source,
+                         u8 enable, qos_bits_t value)
+{
+  switch (input_source)
+    {
+    case QOS_SOURCE_IP:
+      vnet_feature_enable_disable ("ip6-unicast", "ip6-qos-store",
+                                  sw_if_index, enable, &value,
+                                  sizeof (value));
+      vnet_feature_enable_disable ("ip6-multicast", "ip6-qos-store",
+                                  sw_if_index, enable, &value,
+                                  sizeof (value));
+      vnet_feature_enable_disable ("ip4-unicast", "ip4-qos-store",
+                                  sw_if_index, enable, &value,
+                                  sizeof (value));
+      vnet_feature_enable_disable ("ip4-multicast", "ip4-qos-store",
+                                  sw_if_index, enable, &value,
+                                  sizeof (value));
+      break;
+    case QOS_SOURCE_MPLS:
+    case QOS_SOURCE_VLAN:
+    case QOS_SOURCE_EXT:
+      /* not a valid option for storeing */
+      break;
+    }
+}
+
+int
+qos_store_enable (u32 sw_if_index,
+                 qos_source_t input_source, qos_bits_t value)
+{
+  qos_store_t *qst;
+
+  if (QOS_SOURCE_IP != input_source)
+    return VNET_API_ERROR_UNIMPLEMENTED;
+
+  vec_validate (qos_store_configs[input_source], sw_if_index);
+
+  qst = &qos_store_configs[input_source][sw_if_index];
+
+  if (0 == qst->qst_n_cfgs)
+    {
+      qst->qst_value = value;
+      qos_store_feature_config (sw_if_index, input_source, 1, value);
+    }
+
+  qst->qst_n_cfgs++;
+
+  return (0);
+}
+
+int
+qos_store_disable (u32 sw_if_index, qos_source_t input_source)
+{
+  qos_store_t *qst;
+
+  if (vec_len (qos_store_configs[input_source]) <= sw_if_index)
+    return VNET_API_ERROR_NO_MATCHING_INTERFACE;
+
+  qst = &qos_store_configs[input_source][sw_if_index];
+
+  if (0 == qst->qst_n_cfgs)
+    return VNET_API_ERROR_VALUE_EXIST;
+
+  qst->qst_n_cfgs--;
+
+  if (0 == qst->qst_n_cfgs)
+    {
+      qos_store_feature_config (sw_if_index, input_source, 0, qst->qst_value);
+    }
+
+  return (0);
+}
+
+void
+qos_store_walk (qos_store_walk_cb_t fn, void *c)
+{
+  qos_source_t qs;
+
+  FOR_EACH_QOS_SOURCE (qs)
+  {
+    qos_store_t *qst;
+    u32 sw_if_index;
+
+    vec_foreach_index (sw_if_index, qos_store_configs[qs])
+    {
+      qst = &qos_store_configs[qs][sw_if_index];
+      if (0 != qst->qst_n_cfgs)
+       fn (sw_if_index, qs, qst->qst_value, c);
+    }
+  }
+}
+
+/*
+ * Disable storeing feature for all protocols when the interface
+ * is deleted
+ */
+static clib_error_t *
+qos_store_ip_interface_add_del (vnet_main_t * vnm,
+                               u32 sw_if_index, u32 is_add)
+{
+  if (!is_add)
+    {
+      qos_source_t qs;
+
+      FOR_EACH_QOS_SOURCE (qs)
+      {
+       while (qos_store_disable (sw_if_index, qs) == 0);
+      }
+    }
+
+  return (NULL);
+}
+
+VNET_SW_INTERFACE_ADD_DEL_FUNCTION (qos_store_ip_interface_add_del);
+
+clib_error_t *
+qos_store_init (vlib_main_t * vm)
+{
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (qos_store_init);
+
+static clib_error_t *
+qos_store_cli (vlib_main_t * vm,
+              unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  u32 sw_if_index, qs, value;
+  u8 enable;
+
+  qs = 0xff;
+  enable = 1;
+  sw_if_index = ~0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%U", unformat_vnet_sw_interface,
+                   vnm, &sw_if_index))
+       ;
+      else if (unformat (input, "%U", unformat_qos_source, &qs))
+       ;
+      else if (unformat (input, "enable"))
+       enable = 1;
+      else if (unformat (input, "disable"))
+       enable = 0;
+      else if (unformat (input, "value &d", &value))
+       ;
+      else
+       break;
+    }
+
+  if (~0 == sw_if_index)
+    return clib_error_return (0, "interface must be specified");
+  if (0xff == qs)
+    return clib_error_return (0, "input location must be specified");
+
+  if (enable)
+    qos_store_enable (sw_if_index, qs, value);
+  else
+    qos_store_disable (sw_if_index, qs);
+
+  return (NULL);
+}
+
+/*?
+ * Enable QoS bit storeing on an interface using the packet's input DSCP bits
+ * Which input QoS bits to use are either; IP, MPLS or VLAN. If more than
+ * one protocol is chosen (which is foolish) the higher layers override the
+ * lower.
+ *
+ * @cliexpar
+ * @cliexcmd{qos store ip GigEthernet0/1/0}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (qos_store_command, static) = {
+  .path = "qos store",
+  .short_help = "qos store <store-source> <INTERFACE> [disable]",
+  .function = qos_store_cli,
+  .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+static void
+qos_store_show_one_interface (vlib_main_t * vm, u32 sw_if_index)
+{
+  u8 n_cfgs[QOS_N_SOURCES] = { };
+  qos_source_t qs;
+  bool set;
+
+  set = false;
+
+  FOR_EACH_QOS_SOURCE (qs)
+  {
+    if (vec_len (qos_store_configs[qs]) <= sw_if_index)
+      continue;
+    if (0 != (n_cfgs[qs] = qos_store_configs[qs][sw_if_index].qst_n_cfgs))
+      set = true;
+  }
+
+  if (set)
+    {
+      vlib_cli_output (vm, " %U:", format_vnet_sw_if_index_name,
+                      vnet_get_main (), sw_if_index);
+
+      FOR_EACH_QOS_SOURCE (qs)
+      {
+       if (n_cfgs[qs] != 0)
+         vlib_cli_output (vm, "  %U -> %d",
+                          format_qos_source, qs,
+                          qos_store_configs[qs][sw_if_index].qst_value);
+      }
+    }
+}
+
+static clib_error_t *
+qos_store_show (vlib_main_t * vm,
+               unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  qos_source_t qs;
+  u32 sw_if_index;
+
+  sw_if_index = ~0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%U", unformat_vnet_sw_interface,
+                   vnm, &sw_if_index))
+       ;
+    }
+
+  if (~0 == sw_if_index)
+    {
+      u32 ii, n_ints = 0;
+
+      FOR_EACH_QOS_SOURCE (qs)
+      {
+       n_ints = clib_max (n_ints, vec_len (qos_store_configs[qs]));
+      }
+
+      for (ii = 0; ii < n_ints; ii++)
+       {
+         qos_store_show_one_interface (vm, ii);
+       }
+    }
+  else
+    qos_store_show_one_interface (vm, sw_if_index);
+
+  return (NULL);
+}
+
+/*?
+ * Show Egress Qos Maps
+ *
+ * @cliexpar
+ * @cliexcmd{show qos egress map}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (qos_store_show_command, static) = {
+  .path = "show qos store",
+  .short_help = "show qos store [interface]",
+  .function = qos_store_show,
+  .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/qos/qos_store.h b/src/vnet/qos/qos_store.h
new file mode 100644 (file)
index 0000000..7b3c801
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * 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.
+ */
+
+#ifndef __QOS_STORE_H__
+#define __QOS_STORE_H__
+
+#include <vnet/qos/qos_types.h>
+#include <vnet/ip/ip_packet.h>
+
+extern int qos_store_disable (u32 sw_if_index, qos_source_t input_source);
+extern int qos_store_enable (u32 sw_if_index,
+                            qos_source_t input_source, qos_bits_t value);
+
+typedef walk_rc_t (*qos_store_walk_cb_t) (u32 sw_if_index,
+                                         qos_source_t input_source,
+                                         qos_bits_t value, void *ctx);
+void qos_store_walk (qos_store_walk_cb_t fn, void *c);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/qos/qos_store_node.c b/src/vnet/qos/qos_store_node.c
new file mode 100644 (file)
index 0000000..2273b2e
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * 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.
+ */
+
+#include <vnet/qos/qos_store.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ip/ip6_to_ip4.h>
+#include <vnet/feature/feature.h>
+#include <vnet/qos/qos_types.h>
+
+extern u8 *qos_store_configs[QOS_N_SOURCES];
+
+/**
+ * per-packet trace data
+ */
+typedef struct qos_store_trace_t_
+{
+  /* per-pkt trace data */
+  qos_bits_t bits;
+} qos_store_trace_t;
+
+/* packet trace format function */
+static u8 *
+format_qos_store_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  qos_store_trace_t *t = va_arg (*args, qos_store_trace_t *);
+
+  s = format (s, "qos:%d", t->bits);
+
+  return s;
+}
+
+static inline uword
+qos_store_inline (vlib_main_t * vm,
+                 vlib_node_runtime_t * node,
+                 vlib_frame_t * frame, qos_source_t qos_src)
+{
+  u32 n_left_from, *from, *to_next, next_index;
+
+  next_index = 0;
+  n_left_from = frame->n_vectors;
+  from = vlib_frame_vector_args (frame);
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         vlib_buffer_t *b0;
+         u32 next0, bi0;
+         qos_bits_t qos0;
+
+         next0 = 0;
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+
+         qos0 =
+           *(qos_bits_t *) vnet_feature_next_with_data (&next0, b0,
+                                                        sizeof (qos_bits_t));
+
+         vnet_buffer2 (b0)->qos.bits = qos0;
+         vnet_buffer2 (b0)->qos.source = qos_src;
+         b0->flags |= VNET_BUFFER_F_QOS_DATA_VALID;
+
+         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+                            (b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             qos_store_trace_t *t =
+               vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->bits = qos0;
+           }
+
+
+         /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return frame->n_vectors;
+}
+
+
+VLIB_NODE_FN (ip4_qos_store_node) (vlib_main_t * vm,
+                                  vlib_node_runtime_t * node,
+                                  vlib_frame_t * frame)
+{
+  return (qos_store_inline (vm, node, frame, QOS_SOURCE_IP));
+}
+
+VLIB_NODE_FN (ip6_qos_store_node) (vlib_main_t * vm,
+                                  vlib_node_runtime_t * node,
+                                  vlib_frame_t * frame)
+{
+  return (qos_store_inline (vm, node, frame, QOS_SOURCE_IP));
+}
+
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_qos_store_node) = {
+  .name = "ip4-qos-store",
+  .vector_size = sizeof (u32),
+  .format_trace = format_qos_store_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 1,
+
+  .next_nodes = {
+    [0] = "ip4-drop",
+  },
+};
+
+VNET_FEATURE_INIT (ip4_qos_store_node, static) = {
+    .arc_name = "ip4-unicast",
+    .node_name = "ip4-qos-store",
+};
+VNET_FEATURE_INIT (ip4m_qos_store_node, static) = {
+    .arc_name = "ip4-multicast",
+    .node_name = "ip4-qos-store",
+};
+
+VLIB_REGISTER_NODE (ip6_qos_store_node) = {
+  .name = "ip6-qos-store",
+  .vector_size = sizeof (u32),
+  .format_trace = format_qos_store_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 1,
+
+  .next_nodes = {
+    [0] = "ip6-drop",
+  },
+};
+
+VNET_FEATURE_INIT (ip6_qos_store_node, static) = {
+    .arc_name = "ip6-unicast",
+    .node_name = "ip6-qos-store",
+};
+VNET_FEATURE_INIT (ip6m_qos_store_node, static) = {
+    .arc_name = "ip6-multicast",
+    .node_name = "ip6-qos-store",
+};
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index 6b6a1d7..fa9ecda 100644 (file)
@@ -84,6 +84,8 @@
 #include "vom/qos_map_cmds.hpp"
 #include "vom/qos_record.hpp"
 #include "vom/qos_record_cmds.hpp"
+#include "vom/qos_store.hpp"
+#include "vom/qos_store_cmds.hpp"
 
 using namespace boost;
 using namespace VOM;
@@ -508,6 +510,14 @@ public:
                     {
                         rc = handle_derived<QoS::record_cmds::delete_cmd>(f_exp, f_act);
                     }
+                    else if (typeid(*f_exp) == typeid(QoS::store_cmds::create_cmd))
+                    {
+                        rc = handle_derived<QoS::store_cmds::create_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(QoS::store_cmds::delete_cmd))
+                    {
+                        rc = handle_derived<QoS::store_cmds::delete_cmd>(f_exp, f_act);
+                    }
                     else if (typeid(*f_exp) == typeid(QoS::map_cmds::create_cmd))
                     {
                         rc = handle_derived<QoS::map_cmds::create_cmd>(f_exp, f_act);
@@ -2251,16 +2261,24 @@ BOOST_AUTO_TEST_CASE(test_qos) {
     ADD_EXPECT(QoS::record_cmds::create_cmd(hw_qr, hw_ifh.data(), QoS::source_t::IP));
     TRY_CHECK_RC(OM::write(albert, *qr));
 
+    QoS::store *qs = new QoS::store(itf, QoS::source_t::IP, 55);
+    HW::item<bool> hw_qs(true, rc_t::OK);
+    ADD_EXPECT(QoS::store_cmds::create_cmd(hw_qs, hw_ifh.data(), QoS::source_t::IP, 55));
+    TRY_CHECK_RC(OM::write(albert, *qs));
+
     QoS::mark *qm = new QoS::mark(itf, qem, QoS::source_t::IP);
     HW::item<bool> hw_qm(true, rc_t::OK);
     ADD_EXPECT(QoS::mark_cmds::create_cmd(hw_qm, hw_ifh.data(), 1, QoS::source_t::IP));
     TRY_CHECK_RC(OM::write(albert, *qm));
 
+    STRICT_ORDER_OFF();
     delete qr;
     delete qm;
+    delete qs;
     ADD_EXPECT(QoS::mark_cmds::delete_cmd(hw_qm, hw_ifh.data(), QoS::source_t::IP));
     ADD_EXPECT(QoS::map_cmds::delete_cmd(hw_qem, 1));
     ADD_EXPECT(QoS::record_cmds::delete_cmd(hw_qr, hw_ifh.data(), QoS::source_t::IP));
+    ADD_EXPECT(QoS::store_cmds::delete_cmd(hw_qs, hw_ifh.data(), QoS::source_t::IP));
     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf_name));
     TRY_CHECK(OM::remove(albert));
index 9efa798..149a9eb 100644 (file)
@@ -15,7 +15,7 @@ from scapy.layers.inet import IP, UDP
 from scapy.layers.inet6 import IPv6
 from scapy.contrib.mpls import MPLS
 from vpp_papi import VppEnum
-from vpp_qos import VppQosRecord, VppQosEgressMap, VppQosMark
+from vpp_qos import VppQosRecord, VppQosEgressMap, VppQosMark, VppQosStore
 
 NUM_PKTS = 67
 
@@ -63,7 +63,7 @@ class TestQOS(VppTestCase):
         super(TestQOS, self).tearDown()
 
     def test_qos_ip(self):
-        """ QoS Mark/Record IP """
+        """ QoS Mark/Record/Store IP """
 
         #
         # for table 1 map the n=0xff possible values of input QoS mark,
@@ -248,6 +248,32 @@ class TestQOS(VppTestCase):
         self.assertTrue(qr1.query_vpp_config())
         qr1.remove_vpp_config()
 
+        #
+        # back to an unchanged TOS value
+        #
+        rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg1)
+        for p in rx:
+            self.assertEqual(p[IP].tos, 254)
+
+        #
+        # enable QoS stroe instead of record
+        #
+        qst1 = VppQosStore(self, self.pg0,
+                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
+                           5).add_vpp_config()
+        self.logger.info(self.vapi.cli("sh qos store"))
+
+        p_v4[IP].dst = self.pg1.remote_ip4
+        rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg1)
+        for p in rx:
+            self.assertEqual(p[IP].tos, 250)
+
+        #
+        # disable the input storing on pg0
+        #
+        self.assertTrue(qst1.query_vpp_config())
+        qst1.remove_vpp_config()
+
         #
         # back to an unchanged TOS value
         #
index 0577863..a7fa9e7 100644 (file)
@@ -42,6 +42,44 @@ class VppQosRecord(VppObject):
         return ("qos-record-%s-%d" % (self.intf, self.source))
 
 
+class VppQosStore(VppObject):
+    """ QoS Store(ing) configuration """
+
+    def __init__(self, test, intf, source, value):
+        self._test = test
+        self.intf = intf
+        self.source = source
+        self.value = value
+
+    def add_vpp_config(self):
+        self._test.vapi.qos_store_enable_disable(
+            enable=1,
+            store={'sw_if_index': self.intf.sw_if_index,
+                   'input_source': self.source,
+                   'value': self.value})
+        self._test.registry.register(self, self._test.logger)
+        return self
+
+    def remove_vpp_config(self):
+        self._test.vapi.qos_store_enable_disable(
+            enable=0,
+            store={'sw_if_index': self.intf.sw_if_index,
+                   'input_source': self.source})
+
+    def query_vpp_config(self):
+        rs = self._test.vapi.qos_store_dump()
+
+        for r in rs:
+            if self.intf.sw_if_index == r.store.sw_if_index and \
+               self.source == r.store.input_source and \
+               self.value == r.store.value:
+                return True
+        return False
+
+    def object_id(self):
+        return ("qos-store-%s-%d" % (self.intf, self.source))
+
+
 class VppQosEgressMap(VppObject):
     """ QoS Egress Map(ping) configuration """