diff options
author | Konstantin Ananyev <konstantin.ananyev@intel.com> | 2017-02-21 18:12:20 +0000 |
---|---|---|
committer | Konstantin Ananyev <konstantin.ananyev@intel.com> | 2017-02-24 16:37:08 +0000 |
commit | aa97dd1ce910b839fed46ad55d1e70e403f5a930 (patch) | |
tree | f6f0fd494eaf499859bff9f20f5ddfac9ab99233 /lib/libtle_timer/timer.c | |
parent | f5f10013ffef8e4ac1071087b8492fe6380d98fe (diff) |
Introduce first version of TCP code.
Supported functionality:
- open/close
- listen/accept/connect
- send/recv
In order to achieve that libtle_udp library was
reworked into libtle_l4p library that supports
both TCP and UDP protocols.
New libtle_timer library was introduced
(thanks to Cisco guys and Dave Barach <dbarach@cisco.com>
for sharing their timer code with us).
Sample application was also reworked significantly
to support both TCP and UDP traffic handling.
New UT were introduced.
Change-Id: I806b05011f521e89b58db403cfdd484a37beb775
Signed-off-by: Mohammad Abdul Awal <mohammad.abdul.awal@intel.com>
Signed-off-by: Karol Latecki <karolx.latecki@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Diffstat (limited to 'lib/libtle_timer/timer.c')
-rw-r--r-- | lib/libtle_timer/timer.c | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/lib/libtle_timer/timer.c b/lib/libtle_timer/timer.c new file mode 100644 index 0000000..8b89fd6 --- /dev/null +++ b/lib/libtle_timer/timer.c @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2016 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. + */ +/* + * Copyright (c) 2016 Intel Corporation. + * 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 <string.h> +#include <sys/queue.h> +#include <rte_cycles.h> +#include <rte_errno.h> +#include <tle_timer.h> + +#define TW_SLOTS_PER_RING 512 +#define TW_RING_SHIFT 9 +#define TW_RING_MASK (TW_SLOTS_PER_RING - 1) +#define MAX_TIMER_BURST 0x20 + +enum { + TW_RING_FAST, + TW_RING_SLOW, + TW_N_RINGS, +}; + +struct tle_timer_list; + +struct tle_timer_elmt { + void *obj; /** object for which timer is created */ + + struct tle_timer_list *list; /* current list object belongs to */ + + /** Slow ring only, saved when timer added to ring */ + uint16_t fast_index; + + LIST_ENTRY(tle_timer_elmt) link; +}; + +struct tle_timer_list { + uint32_t num; + LIST_HEAD(, tle_timer_elmt) head; +}; + +struct tle_timer_wheel { + uint64_t next_run_time; /** Next time the wheel should run */ + + uint64_t last_run_time; /** Last time the wheel ran */ + + uint32_t current_tick; /** current tick */ + + uint32_t current_index[TW_N_RINGS]; /** current wheel indices */ + + struct tle_timer_list free; /** free timers to be used */ + + struct tle_timer_list expired; /** expired timers to be pulled */ + + struct tle_timer_wheel_args prm; /** timer wheel configuration params */ + + /** wheel arrays */ + struct tle_timer_list w[TW_N_RINGS][TW_SLOTS_PER_RING]; +}; + +/** helper functions to manipulate the linked lists */ +static inline uint32_t +get_timers(struct tle_timer_list *list, struct tle_timer_elmt *re[], + uint32_t num) +{ + struct tle_timer_elmt *e; + uint32_t i, n; + + n = RTE_MIN(list->num, num); + for (i = 0; i != n; i++) { + e = LIST_FIRST(&list->head); + LIST_REMOVE(e, link); + e->list = NULL; + re[i] = e; + } + + list->num -= n; + return n; +} + +static inline struct tle_timer_elmt * +get_timer(struct tle_timer_list *list) +{ + struct tle_timer_elmt *e; + + e = LIST_FIRST(&list->head); + LIST_REMOVE(e, link); + e->list = NULL; + list->num--; + return e; +} + +static inline void +put_timers(struct tle_timer_list *list, struct tle_timer_elmt *te[], + uint32_t num) +{ + uint32_t i; + + for (i = 0; i != num; i++) { + te[i]->list = list; + LIST_INSERT_HEAD(&list->head, te[i], link); + } + list->num += num; +} + +static inline void +put_timer(struct tle_timer_list *list, struct tle_timer_elmt *e) +{ + e->list = list; + LIST_INSERT_HEAD(&list->head, e, link); + list->num++; +} + +static inline void +rem_timer(struct tle_timer_list *list, struct tle_timer_elmt *e) +{ + LIST_REMOVE(e, link); + e->list = NULL; + list->num--; +} + +/** create the tle timer wheel */ +struct tle_timer_wheel * +tle_timer_create(struct tle_timer_wheel_args *prm, uint64_t now) +{ + uint32_t i, j; + size_t sz; + struct tle_timer_wheel *tw; + struct tle_timer_elmt *e; + struct tle_timer_elmt *timers; + + if (prm == NULL) { + rte_errno = -EINVAL; + return NULL; + } + + /* at least one timer has to be created */ + if (prm->max_timer == 0) { + rte_errno = -EINVAL; + return NULL; + } + + /* do not allow tick size smaller than 1ms */ + if (prm->tick_size == 0) { + rte_errno = -EINVAL; + return NULL; + } + + sz = sizeof(*tw) + prm->max_timer * sizeof(struct tle_timer_elmt); + + /* allocate memory */ + tw = rte_zmalloc_socket(NULL, sz, RTE_CACHE_LINE_SIZE, + prm->socket_id); + + if (tw == NULL) { + rte_errno = -ENOMEM; + return NULL; + } + + tw->last_run_time = now; + tw->prm = *prm; + timers = (struct tle_timer_elmt *)(tw + 1); + + /* initialize the lists */ + LIST_INIT(&tw->free.head); + LIST_INIT(&tw->expired.head); + + for (i = 0; i < prm->max_timer; i++) { + e = timers + i; + put_timer(&tw->free, e); + } + + for (i = 0; i < TW_N_RINGS; i++) + for (j = 0; j < TW_SLOTS_PER_RING; j++) + LIST_INIT(&tw->w[i][j].head); + + return tw; +} + +/** free the tle timer wheel */ +void +tle_timer_free(struct tle_timer_wheel *tw) +{ + rte_free(tw); +} + +/** start a timer */ +void * +tle_timer_start(struct tle_timer_wheel *tw, void *obj, uint64_t interval) +{ + uint16_t slow_ring_index, fast_ring_index; + struct tle_timer_list *ts; + struct tle_timer_elmt *e; + uint32_t carry; + uint32_t nb_tick; + + rte_errno = 0; + if (!interval) { + rte_errno = EINVAL; + return NULL; + } + + if (tw->free.num == 0) { + rte_errno = ENOMEM; + return NULL; + } + + nb_tick = interval / tw->prm.tick_size; + + fast_ring_index = nb_tick & TW_RING_MASK; + fast_ring_index += tw->current_index[TW_RING_FAST]; + carry = fast_ring_index >= TW_SLOTS_PER_RING ? 1 : 0; + fast_ring_index %= TW_SLOTS_PER_RING; + slow_ring_index = (nb_tick >> TW_RING_SHIFT) + carry; + + /* Timer duration exceeds ~7 hrs? Oops */ + if (slow_ring_index >= TW_SLOTS_PER_RING) { + rte_errno = ERANGE; + return NULL; + } + + /* Timer expires more than 51.2 seconds from now? */ + if (slow_ring_index) { + slow_ring_index += tw->current_index[TW_RING_SLOW]; + slow_ring_index %= TW_SLOTS_PER_RING; + ts = &tw->w[TW_RING_SLOW][slow_ring_index]; + + e = get_timer(&tw->free); + e->obj = obj; + e->fast_index = fast_ring_index; + put_timer(ts, e); + + /* Return the user timer-cancellation handle */ + return (void *)e; + } + + /* Timer expires less than 51.2 seconds from now */ + ts = &tw->w[TW_RING_FAST][fast_ring_index]; + + e = get_timer(&tw->free); + e->obj = obj; + put_timer(ts, e); + + /* Give the user a handle to cancel the timer */ + return (void *)e; +} + +/** stop a timer */ +void tle_timer_stop(struct tle_timer_wheel *tw, void *timer) +{ + struct tle_timer_elmt *e; + struct tle_timer_list *ts; + + /* Cancel the timer */ + e = (struct tle_timer_elmt *)timer; + ts = e->list; + rem_timer(ts, e); + put_timer(&tw->free, e); +} + +/** run the timer wheel. Call in every tick_size cycles + * (e.g. equivalent of 100ms). + */ +void tle_timer_expire(struct tle_timer_wheel *tw, uint64_t now) +{ + uint32_t nb_tick, i, n; + uint32_t fast_wheel_index, slow_wheel_index, demoted_index; + struct tle_timer_list *ts, *ts2; + struct tle_timer_elmt *re[MAX_TIMER_BURST], *e; + + /* Shouldn't happen */ + if (unlikely(now < tw->next_run_time)) + return; + + /* Number of tick_size cycles which have occurred */ + nb_tick = (now - tw->last_run_time) / tw->prm.tick_size; + if (nb_tick == 0) + return; + + /* Remember when we ran, compute next runtime */ + tw->next_run_time = (now + tw->prm.tick_size); + tw->last_run_time = now; + + for (i = 0; i < nb_tick; i++) { + fast_wheel_index = tw->current_index[TW_RING_FAST]; + + /* If we've been around the fast ring once, + * process one slot in the slow ring before we handle + * the fast ring. + */ + if (unlikely(fast_wheel_index == TW_SLOTS_PER_RING)) { + fast_wheel_index = tw->current_index[TW_RING_FAST] = 0; + + tw->current_index[TW_RING_SLOW]++; + tw->current_index[TW_RING_SLOW] %= TW_SLOTS_PER_RING; + slow_wheel_index = tw->current_index[TW_RING_SLOW]; + + ts = &tw->w[TW_RING_SLOW][slow_wheel_index]; + + /* Deal slow-ring elements into the fast ring. */ + while (ts->num != 0) { + e = get_timer(ts); + demoted_index = e->fast_index; + ts2 = &tw->w[TW_RING_FAST][demoted_index]; + put_timer(ts2, e); + }; + LIST_INIT(&ts->head); + } + + /* Handle the fast ring */ + ts = &tw->w[TW_RING_FAST][fast_wheel_index]; + + /* Clear the fast-ring slot and move timers in expired list*/ + n = get_timers(ts, re, RTE_DIM(re)); + while (n != 0) { + put_timers(&tw->expired, re, n); + n = get_timers(ts, re, RTE_DIM(re)); + }; + LIST_INIT(&ts->head); + + tw->current_index[TW_RING_FAST]++; + tw->current_tick++; + } +} + +/** bulk retrieve of expired timers */ +int +tle_timer_get_expired_bulk(struct tle_timer_wheel *tw, void *rt[], uint32_t num) +{ + uint32_t i, n; + struct tle_timer_elmt *e[MAX_TIMER_BURST]; + + n = get_timers(&tw->expired, e, num); + + for (i = 0; i != n; i++) + rt[i] = e[i]->obj; + + put_timers(&tw->free, e, n); + + return n; +} |