aboutsummaryrefslogtreecommitdiffstats
path: root/cicn-plugin/cicn/cicn_pcs.h
diff options
context:
space:
mode:
Diffstat (limited to 'cicn-plugin/cicn/cicn_pcs.h')
-rw-r--r--cicn-plugin/cicn/cicn_pcs.h512
1 files changed, 512 insertions, 0 deletions
diff --git a/cicn-plugin/cicn/cicn_pcs.h b/cicn-plugin/cicn/cicn_pcs.h
new file mode 100644
index 00000000..0c6f9237
--- /dev/null
+++ b/cicn-plugin/cicn/cicn_pcs.h
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+/*
+ * cicn_pcs.h: Opportunistic timeout code for the PIT/CS used in the cicn forwarder.
+ */
+
+#ifndef _CICN_PCS_H_
+#define _CICN_PCS_H_ 1
+
+#if !CICN_VPP_PLUGIN
+#error "cicn-internal file included externally"
+#endif
+
+#include "cicn_hashtb.h"
+
+/* The PIT and CS are stored as a union */
+#define CICN_PIT_NULL_TYPE 0
+#define CICN_PIT_TYPE 1
+#define CICN_CS_TYPE 2
+
+/* Max number of incoming (interest) faces supported, for now. Note that changing
+ * this may change alignment within the PIT struct, so be careful.
+ */
+typedef struct cicn_pcs_shared_s
+{
+
+ /* Installation/creation time (vpp float units, for now) */
+ f64 create_time;
+
+ /* Expiration time (vpp float units, for now) */
+ f64 expire_time;
+
+ /* Shared 'type' octet */
+ uint8_t entry_type;
+
+ /* Shared 'flags' octet */
+ uint8_t entry_flags;
+
+ /* Shared size 8 + 8 + 2 = 18B */
+
+} cicn_pcs_shared_t;
+
+/*
+ * PIT entry, unioned with a CS entry below
+ */
+typedef struct cicn_pit_entry_s
+{
+
+ /* Shared size 8 + 8 + 2 = 18B */
+
+ /* Egress face and array of ingress faces */
+ /* 18B + 2B*8 = 34B */
+ uint16_t pe_txface;
+ uint16_t pe_rxfaces[CICN_PARAM_PIT_ENTRY_PHOPS_MAX];
+
+ /* Bitmap of FIB faces tried (TODO -- needed in first go?) */
+ /* 34 + 2B = 36B */
+ uint16_t pe_tx_face_map;
+
+ /* FIB entry id (TODO -- why - related to 'faces tried'?) */
+ /* 36 + 4B = 40B */
+ uint32_t pe_fib_idx;
+
+ /* Packet buffer idx, if held */
+ /* 40 + 4B = 44B */
+ uint32_t pe_pkt_buf;
+
+} cicn_pit_entry_t;
+
+/*
+ * CS entry, unioned with a PIT entry below
+ */
+typedef struct cicn_cs_entry_s
+{
+
+ /* Shared size 8 + 8 + 2 = 18B */
+
+ /* Ingress face */
+ /* 2B = 20B */
+ uint16_t cs_rxface;
+
+ /* Packet buffer, if held */
+ /* 4B = 24B */
+ uint32_t cs_pkt_buf;
+
+ /* Linkage for LRU, in the form of hashtable node indexes */
+ /* 8B = 32B */
+ uint32_t cs_lru_prev;
+ uint32_t cs_lru_next;
+
+} cicn_cs_entry_t;
+
+/*
+ * Combined PIT/CS entry data structure, embedded in a hashtable entry
+ * after the common hashtable preamble struct. This MUST fit in the available
+ * (fixed) space in a hashtable node.
+ */
+typedef struct cicn_pcs_entry_s
+{
+
+ cicn_pcs_shared_t shared;
+
+ union
+ {
+ cicn_pit_entry_t pit;
+ cicn_cs_entry_t cs;
+ } u;
+} cicn_pcs_entry_t;
+
+/*
+ * Overall PIT/CS table, based on the common hashtable
+ */
+typedef struct cicn_pit_cs_s
+{
+
+ cicn_hashtb_t *pcs_table;
+
+ /* Counters for PIT/CS entries */
+ uint32_t pcs_pit_count;
+ uint32_t pcs_cs_count;
+
+ /* TODO -- CS LRU, represented as ... */
+ uint32_t pcs_lru_max;
+ uint32_t pcs_lru_count;
+
+ /* Indexes to hashtable nodes forming CS LRU */
+ uint32_t pcs_lru_head;
+ uint32_t pcs_lru_tail;
+
+} cicn_pit_cs_t;
+
+/* Accessor for pit/cs data inside hash table node */
+static inline cicn_pcs_entry_t *
+cicn_pit_get_data (cicn_hash_node_t * node)
+{
+ return (cicn_pcs_entry_t *) (cicn_hashtb_node_data (node));
+}
+
+/* Init pit/cs data block (usually inside hash table node) */
+static inline void
+cicn_pit_init_data (cicn_pcs_entry_t * p)
+{
+ memset (p, 0, sizeof (cicn_pcs_entry_t));
+}
+
+/* Wrapper for init/alloc of a new pit/cs */
+static inline int
+cicn_pit_create (cicn_pit_cs_t * p, uint32_t num_elems)
+{
+ int ret =
+ cicn_hashtb_alloc (&p->pcs_table, num_elems, sizeof (cicn_pcs_entry_t));
+ p->pcs_table->ht_flags |= CICN_HASHTB_FLAG_KEY_FMT_NAME;
+
+ p->pcs_pit_count = p->pcs_cs_count = 0;
+
+ p->pcs_lru_max = CICN_PARAM_CS_LRU_DEFAULT;
+ p->pcs_lru_count = 0;
+ p->pcs_lru_head = p->pcs_lru_tail = 0;
+
+ return (ret);
+}
+
+static inline f64
+cicn_pcs_get_exp_time (f64 cur_time_sec, uint64_t lifetime_msec)
+{
+ return (cur_time_sec + ((f64) lifetime_msec) / SEC_MS);
+}
+
+/*
+ * Configure CS LRU limit. Zero is accepted, means 'no limit', probably not
+ * a good choice.
+ */
+static inline void
+cicn_pit_set_lru_max (cicn_pit_cs_t * p, uint32_t limit)
+{
+ p->pcs_lru_max = limit;
+}
+
+/*
+ * Accessor for PIT interest counter.
+ */
+static inline uint32_t
+cicn_pit_get_int_count (const cicn_pit_cs_t * pitcs)
+{
+ return (pitcs->pcs_pit_count);
+}
+
+/*
+ * Accessor for PIT cs entries counter.
+ */
+static inline uint32_t
+cicn_pit_get_cs_count (const cicn_pit_cs_t * pitcs)
+{
+ return (pitcs->pcs_cs_count);
+}
+
+/*
+ * Convert a PIT entry into a CS entry (assumes that the entry is already
+ * in the hashtable.)
+ * This is primarily here to maintain the internal counters.
+ */
+static inline int
+cicn_pit_to_cs (cicn_pit_cs_t * p, cicn_pcs_entry_t * pcs)
+{
+ ASSERT (pcs->shared.entry_type = CICN_PIT_TYPE);
+
+ pcs->shared.entry_type = CICN_CS_TYPE;
+
+ p->pcs_pit_count--;
+ p->pcs_cs_count++;
+
+ return (AOK);
+}
+
+/*
+ * Is CS enabled?
+ * CICN_FEATURE_CS is tri-valued: value of 2 means do a run-time check
+ */
+static inline int
+cicn_cs_enabled (cicn_pit_cs_t * pit)
+{
+ switch (CICN_FEATURE_CS)
+ {
+ case 0:
+ default:
+ return (0);
+ case 1:
+ return (1);
+ case 2:
+ return (pit->pcs_lru_max > 0);
+ }
+}
+
+/*
+ * Insert a new CS element at the head of the CS LRU
+ */
+static inline void
+cicn_cs_lru_insert (cicn_pit_cs_t * p, cicn_hash_node_t * pnode,
+ cicn_pcs_entry_t * pcs)
+{
+ cicn_hash_node_t *lrunode;
+ cicn_pcs_entry_t *lrupcs;
+ uint32_t idx;
+
+ idx = cicn_hashtb_node_idx_from_node (p->pcs_table, pnode);
+
+ if (p->pcs_lru_head != 0)
+ {
+ lrunode = cicn_hashtb_node_from_idx (p->pcs_table, p->pcs_lru_head);
+ lrupcs = cicn_pit_get_data (lrunode);
+
+ ASSERT (lrupcs->u.cs.cs_lru_prev == 0);
+ lrupcs->u.cs.cs_lru_prev = idx;
+
+ pcs->u.cs.cs_lru_prev = 0;
+ pcs->u.cs.cs_lru_next = p->pcs_lru_head;
+
+ p->pcs_lru_head = idx;
+
+ }
+ else
+ {
+ ASSERT (p->pcs_lru_tail == 0); /* We think the list is empty */
+
+ p->pcs_lru_head = p->pcs_lru_tail = idx;
+
+ pcs->u.cs.cs_lru_next = pcs->u.cs.cs_lru_prev = 0;
+ }
+
+ p->pcs_lru_count++;
+}
+
+/*
+ * Dequeue an LRU element, for example when it has expired.
+ */
+static inline void
+cicn_cs_lru_dequeue (cicn_pit_cs_t * pit, cicn_hash_node_t * pnode,
+ cicn_pcs_entry_t * pcs)
+{
+ cicn_hash_node_t *lrunode;
+ cicn_pcs_entry_t *lrupcs;
+
+ if (pcs->u.cs.cs_lru_prev != 0)
+ {
+ /* Not already on the head of the LRU */
+ lrunode = cicn_hashtb_node_from_idx (pit->pcs_table,
+ pcs->u.cs.cs_lru_prev);
+ lrupcs = cicn_pit_get_data (lrunode);
+
+ lrupcs->u.cs.cs_lru_next = pcs->u.cs.cs_lru_next;
+ }
+ else
+ {
+ ASSERT (pit->pcs_lru_head ==
+ cicn_hashtb_node_idx_from_node (pit->pcs_table, pnode));
+ pit->pcs_lru_head = pcs->u.cs.cs_lru_next;
+ }
+
+ if (pcs->u.cs.cs_lru_next != 0)
+ {
+ /* Not already the end of the LRU */
+ lrunode = cicn_hashtb_node_from_idx (pit->pcs_table,
+ pcs->u.cs.cs_lru_next);
+ lrupcs = cicn_pit_get_data (lrunode);
+
+ lrupcs->u.cs.cs_lru_prev = pcs->u.cs.cs_lru_prev;
+ }
+ else
+ {
+ /* This was the last LRU element */
+ ASSERT (pit->pcs_lru_tail ==
+ cicn_hashtb_node_idx_from_node (pit->pcs_table, pnode));
+ pit->pcs_lru_tail = pcs->u.cs.cs_lru_prev;
+ }
+
+ pit->pcs_lru_count -= 1;
+}
+
+/*
+ * Move a CS LRU element to the head, probably after it's been used.
+ */
+static inline void
+cicn_cs_lru_update_head (cicn_pit_cs_t * pit, cicn_hash_node_t * pnode,
+ cicn_pcs_entry_t * pcs)
+{
+
+ if (pcs->u.cs.cs_lru_prev != 0)
+ {
+ /* Not already on the head of the LRU, detach it from its current
+ * position
+ */
+ cicn_cs_lru_dequeue (pit, pnode, pcs);
+
+ /* Now detached from the list; attach at head */
+ cicn_cs_lru_insert (pit, pnode, pcs);
+
+ }
+ else
+ {
+ ASSERT (pit->pcs_lru_head ==
+ cicn_hashtb_node_idx_from_node (pit->pcs_table, pnode));
+ }
+}
+
+/*
+ * Remove a batch of nodes from the CS LRU, copying their node indexes into
+ * the caller's array. We expect this is done when the LRU size exceeds
+ * the CS's limit.
+ */
+static inline int
+cicn_cs_lru_trim (cicn_pit_cs_t * pit, uint32_t * node_list, int sz)
+{
+ cicn_hash_node_t *lrunode;
+ cicn_pcs_entry_t *lrupcs;
+ uint32_t idx;
+ int i;
+
+ idx = pit->pcs_lru_tail;
+
+ for (i = 0; i < sz; i++)
+ {
+
+ if (idx == 0)
+ {
+ break;
+ }
+
+ lrunode = cicn_hashtb_node_from_idx (pit->pcs_table, idx);
+ lrupcs = cicn_pit_get_data (lrunode);
+
+ node_list[i] = idx;
+
+ idx = lrupcs->u.cs.cs_lru_prev;
+ }
+
+ pit->pcs_lru_count -= i;
+
+ pit->pcs_lru_tail = idx;
+ if (idx != 0)
+ {
+ lrunode = cicn_hashtb_node_from_idx (pit->pcs_table, idx);
+ lrupcs = cicn_pit_get_data (lrunode);
+
+ lrupcs->u.cs.cs_lru_next = 0;
+ }
+ else
+ {
+ /* If the tail is empty, the whole lru is empty */
+ pit->pcs_lru_head = 0;
+ }
+
+ return (i);
+}
+
+/*
+ * Insert PIT/CS entry into the hashtable
+ * The main purpose of this wrapper is helping maintain the per-PIT stats.
+ */
+static inline int
+cicn_pit_insert (cicn_pit_cs_t * pitcs, cicn_pcs_entry_t * entry,
+ cicn_hash_node_t * node)
+{
+ int ret;
+
+ ASSERT (entry == cicn_hashtb_node_data (node));
+
+ ret = cicn_hashtb_insert (pitcs->pcs_table, node);
+ if (ret == AOK)
+ {
+ if (entry->shared.entry_type == CICN_PIT_TYPE)
+ {
+ pitcs->pcs_pit_count++;
+ }
+ else
+ {
+ pitcs->pcs_cs_count++;
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * Delete a PIT/CS entry from the hashtable, freeing the hash node struct.
+ * The caller's pointers are zeroed!
+ * If cs_trim is true, entry has already been removed from lru list
+ * The main purpose of this wrapper is helping maintain the per-PIT stats.
+ */
+static inline int
+cicn_pcs_delete_internal (cicn_pit_cs_t * pitcs,
+ cicn_pcs_entry_t ** pcs_entryp,
+ cicn_hash_node_t ** nodep, vlib_main_t * vm,
+ int cs_trim)
+{
+ int ret;
+ cicn_pcs_entry_t *pcs = *pcs_entryp;
+
+ ASSERT (pcs == cicn_hashtb_node_data (*nodep));
+
+ if (pcs->shared.entry_type == CICN_PIT_TYPE)
+ {
+ pitcs->pcs_pit_count--;
+ }
+ else
+ {
+ pitcs->pcs_cs_count--;
+ // Clean up LRU queue unless entry already removed by bulk CS LRU trim
+ if (!cs_trim)
+ {
+ cicn_cs_lru_dequeue (pitcs, *nodep, pcs);
+ }
+ /* Free any associated packet buffer */
+ if (pcs->u.cs.cs_pkt_buf != 0)
+ {
+ BUFTRC ("PCS-DEL", pcs->u.cs.cs_pkt_buf);
+ vlib_buffer_free_one (vm, pcs->u.cs.cs_pkt_buf);
+ pcs->u.cs.cs_pkt_buf = 0;
+ }
+ }
+
+ ret = cicn_hashtb_delete (pitcs->pcs_table, nodep);
+ *pcs_entryp = NULL;
+
+ return (ret);
+}
+
+/*
+ * Delete entry normally
+ */
+static inline int
+cicn_pcs_delete (cicn_pit_cs_t * pitcs, cicn_pcs_entry_t ** pcs_entryp,
+ cicn_hash_node_t ** nodep, vlib_main_t * vm)
+{
+ return (cicn_pcs_delete_internal
+ (pitcs, pcs_entryp, nodep, vm, 0 /*!cs_trim */ ));
+
+}
+
+/*
+ * Delete entry which has already been bulk-removed from lru list
+ */
+static inline int
+cicn_cs_delete_trimmed (cicn_pit_cs_t * pitcs, cicn_pcs_entry_t ** pcs_entryp,
+ cicn_hash_node_t ** nodep, vlib_main_t * vm)
+{
+ return (cicn_pcs_delete_internal
+ (pitcs, pcs_entryp, nodep, vm, 1 /*cs_trim */ ));
+
+}
+
+/*
+ * Other than the hash value, the arguments are all context needed
+ * to update the worker context.
+ */
+int
+cicn_pcs_timeout (vlib_main_t * vm,
+ cicn_pit_cs_t * pitcs, uint64_t h, u32 * pec, u32 * cec);
+
+
+#endif /* _CICN_PCS_H_ */