get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/27813/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 27813,
    "url": "http://patches.dpdk.org/api/patches/27813/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/1503499644-29432-2-git-send-email-erik.g.carrillo@intel.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<1503499644-29432-2-git-send-email-erik.g.carrillo@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1503499644-29432-2-git-send-email-erik.g.carrillo@intel.com",
    "date": "2017-08-23T14:47:22",
    "name": "[dpdk-dev,1/3] timer: add per-installer pending lists for each lcore",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "49f1037102702846e6d6d50c1a8bd7c831cdc642",
    "submitter": {
        "id": 762,
        "url": "http://patches.dpdk.org/api/people/762/?format=api",
        "name": "Carrillo, Erik G",
        "email": "erik.g.carrillo@intel.com"
    },
    "delegate": null,
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/1503499644-29432-2-git-send-email-erik.g.carrillo@intel.com/mbox/",
    "series": [],
    "comments": "http://patches.dpdk.org/api/patches/27813/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/27813/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@dpdk.org",
        "Delivered-To": "patchwork@dpdk.org",
        "Received": [
            "from [92.243.14.124] (localhost [IPv6:::1])\n\tby dpdk.org (Postfix) with ESMTP id 703E47D53;\n\tWed, 23 Aug 2017 16:47:44 +0200 (CEST)",
            "from mga04.intel.com (mga04.intel.com [192.55.52.120])\n\tby dpdk.org (Postfix) with ESMTP id F05727D36\n\tfor <dev@dpdk.org>; Wed, 23 Aug 2017 16:47:41 +0200 (CEST)",
            "from orsmga002.jf.intel.com ([10.7.209.21])\n\tby fmsmga104.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t23 Aug 2017 07:47:41 -0700",
            "from txasoft-yocto.an.intel.com (HELO txasoft-yocto.an.intel.com.)\n\t([10.123.72.111])\n\tby orsmga002.jf.intel.com with ESMTP; 23 Aug 2017 07:47:40 -0700"
        ],
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.41,417,1498546800\"; d=\"scan'208\";a=\"127405080\"",
        "From": "Gabriel Carrillo <erik.g.carrillo@intel.com>",
        "To": "rsanford@akamai.com",
        "Cc": "dev@dpdk.org",
        "Date": "Wed, 23 Aug 2017 09:47:22 -0500",
        "Message-Id": "<1503499644-29432-2-git-send-email-erik.g.carrillo@intel.com>",
        "X-Mailer": "git-send-email 1.7.10",
        "In-Reply-To": "<1503499644-29432-1-git-send-email-erik.g.carrillo@intel.com>",
        "References": "<1503499644-29432-1-git-send-email-erik.g.carrillo@intel.com>",
        "Subject": "[dpdk-dev] [PATCH 1/3] timer: add per-installer pending lists for\n\teach lcore",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<http://dpdk.org/ml/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://dpdk.org/ml/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<http://dpdk.org/ml/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "Instead of each priv_timer struct containing a single skiplist, this\ncommit adds a skiplist for each enabled lcore to priv_timer. In the case\nthat multiple lcores repeatedly install timers on the same target lcore,\nthis change reduces lock contention for the target lcore's skiplists and\nincreases performance.\n\nSigned-off-by: Gabriel Carrillo <erik.g.carrillo@intel.com>\n---\n lib/librte_timer/rte_timer.c | 309 +++++++++++++++++++++++++++----------------\n lib/librte_timer/rte_timer.h |   9 +-\n 2 files changed, 202 insertions(+), 116 deletions(-)",
    "diff": "diff --git a/lib/librte_timer/rte_timer.c b/lib/librte_timer/rte_timer.c\nindex 5ee0840..2db74c1 100644\n--- a/lib/librte_timer/rte_timer.c\n+++ b/lib/librte_timer/rte_timer.c\n@@ -37,6 +37,7 @@\n #include <inttypes.h>\n #include <assert.h>\n #include <sys/queue.h>\n+#include <stdbool.h>\n \n #include <rte_atomic.h>\n #include <rte_common.h>\n@@ -56,17 +57,19 @@\n \n LIST_HEAD(rte_timer_list, rte_timer);\n \n+struct skiplist {\n+\tstruct rte_timer head;  /**< dummy timer instance to head up list */\n+\trte_spinlock_t lock;\t/**< lock to protect list access */\n+\tunsigned depth;\t\t/**< track the current depth of the skiplist */\n+} __rte_cache_aligned;\n+\n struct priv_timer {\n-\tstruct rte_timer pending_head;  /**< dummy timer instance to head up list */\n-\trte_spinlock_t list_lock;       /**< lock to protect list access */\n+\t/** one pending list per enabled lcore */\n+\tstruct skiplist pending_lists[RTE_MAX_LCORE];\n \n \t/** per-core variable that true if a timer was updated on this\n \t *  core since last reset of the variable */\n \tint updated;\n-\n-\t/** track the current depth of the skiplist */\n-\tunsigned curr_skiplist_depth;\n-\n \tunsigned prev_lcore;              /**< used for lcore round robin */\n \n \t/** running timer on this lcore now */\n@@ -81,6 +84,10 @@ struct priv_timer {\n /** per-lcore private info for timers */\n static struct priv_timer priv_timer[RTE_MAX_LCORE];\n \n+/** cache of IDs of enabled lcores */\n+static unsigned enabled_lcores[RTE_MAX_LCORE];\n+static int n_enabled_lcores;\n+\n /* when debug is enabled, store some statistics */\n #ifdef RTE_LIBRTE_TIMER_DEBUG\n #define __TIMER_STAT_ADD(name, n) do {\t\t\t\t\t\\\n@@ -96,14 +103,25 @@ static struct priv_timer priv_timer[RTE_MAX_LCORE];\n void\n rte_timer_subsystem_init(void)\n {\n-\tunsigned lcore_id;\n+\tunsigned lcore_id1, lcore_id2;\n+\tstruct skiplist *list;\n+\tint i, j;\n+\n+\tRTE_LCORE_FOREACH(lcore_id1)\n+\t\tenabled_lcores[n_enabled_lcores++] = lcore_id1;\n \n \t/* since priv_timer is static, it's zeroed by default, so only init some\n \t * fields.\n \t */\n-\tfor (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id ++) {\n-\t\trte_spinlock_init(&priv_timer[lcore_id].list_lock);\n-\t\tpriv_timer[lcore_id].prev_lcore = lcore_id;\n+\tfor (i = 0, lcore_id1 = enabled_lcores[i]; i < n_enabled_lcores;\n+\t     lcore_id1 = enabled_lcores[++i]) {\n+\t\tpriv_timer[lcore_id1].prev_lcore = lcore_id1;\n+\n+\t\tfor (j = 0, lcore_id2 = enabled_lcores[j]; j < n_enabled_lcores;\n+\t\t     lcore_id2 = enabled_lcores[++j]) {\n+\t\t\tlist = &priv_timer[lcore_id1].pending_lists[lcore_id2];\n+\t\t\trte_spinlock_init(&list->lock);\n+\t\t}\n \t}\n }\n \n@@ -114,7 +132,8 @@ rte_timer_init(struct rte_timer *tim)\n \tunion rte_timer_status status;\n \n \tstatus.state = RTE_TIMER_STOP;\n-\tstatus.owner = RTE_TIMER_NO_OWNER;\n+\tstatus.installer = RTE_TIMER_NO_LCORE;\n+\tstatus.owner = RTE_TIMER_NO_LCORE;\n \ttim->status.u32 = status.u32;\n }\n \n@@ -142,7 +161,7 @@ timer_set_config_state(struct rte_timer *tim,\n \t\t * or ready to run on local core, exit\n \t\t */\n \t\tif (prev_status.state == RTE_TIMER_RUNNING &&\n-\t\t    (prev_status.owner != (uint16_t)lcore_id ||\n+\t\t    (prev_status.owner != (int)lcore_id ||\n \t\t     tim != priv_timer[lcore_id].running_tim))\n \t\t\treturn -1;\n \n@@ -153,7 +172,8 @@ timer_set_config_state(struct rte_timer *tim,\n \t\t/* here, we know that timer is stopped or pending,\n \t\t * mark it atomically as being configured */\n \t\tstatus.state = RTE_TIMER_CONFIG;\n-\t\tstatus.owner = (int16_t)lcore_id;\n+\t\tstatus.installer = RTE_TIMER_NO_LCORE;\n+\t\tstatus.owner = lcore_id;\n \t\tsuccess = rte_atomic32_cmpset(&tim->status.u32,\n \t\t\t\t\t      prev_status.u32,\n \t\t\t\t\t      status.u32);\n@@ -185,7 +205,8 @@ timer_set_running_state(struct rte_timer *tim)\n \t\t/* here, we know that timer is stopped or pending,\n \t\t * mark it atomically as being configured */\n \t\tstatus.state = RTE_TIMER_RUNNING;\n-\t\tstatus.owner = (int16_t)lcore_id;\n+\t\tstatus.installer = prev_status.installer;\n+\t\tstatus.owner = lcore_id;\n \t\tsuccess = rte_atomic32_cmpset(&tim->status.u32,\n \t\t\t\t\t      prev_status.u32,\n \t\t\t\t\t      status.u32);\n@@ -236,11 +257,11 @@ timer_get_skiplist_level(unsigned curr_depth)\n  * are <= that time value.\n  */\n static void\n-timer_get_prev_entries(uint64_t time_val, unsigned tim_lcore,\n+timer_get_prev_entries(uint64_t time_val, struct skiplist *list,\n \t\tstruct rte_timer **prev)\n {\n-\tunsigned lvl = priv_timer[tim_lcore].curr_skiplist_depth;\n-\tprev[lvl] = &priv_timer[tim_lcore].pending_head;\n+\tunsigned lvl = list->depth;\n+\tprev[lvl] = &list->head;\n \twhile(lvl != 0) {\n \t\tlvl--;\n \t\tprev[lvl] = prev[lvl+1];\n@@ -255,15 +276,15 @@ timer_get_prev_entries(uint64_t time_val, unsigned tim_lcore,\n  * all skiplist levels.\n  */\n static void\n-timer_get_prev_entries_for_node(struct rte_timer *tim, unsigned tim_lcore,\n+timer_get_prev_entries_for_node(struct rte_timer *tim, struct skiplist *list,\n \t\tstruct rte_timer **prev)\n {\n \tint i;\n \t/* to get a specific entry in the list, look for just lower than the time\n \t * values, and then increment on each level individually if necessary\n \t */\n-\ttimer_get_prev_entries(tim->expire - 1, tim_lcore, prev);\n-\tfor (i = priv_timer[tim_lcore].curr_skiplist_depth - 1; i >= 0; i--) {\n+\ttimer_get_prev_entries(tim->expire - 1, list, prev);\n+\tfor (i = list->depth - 1; i >= 0; i--) {\n \t\twhile (prev[i]->sl_next[i] != NULL &&\n \t\t\t\tprev[i]->sl_next[i] != tim &&\n \t\t\t\tprev[i]->sl_next[i]->expire <= tim->expire)\n@@ -282,25 +303,25 @@ timer_add(struct rte_timer *tim, unsigned tim_lcore, int local_is_locked)\n \tunsigned lcore_id = rte_lcore_id();\n \tunsigned lvl;\n \tstruct rte_timer *prev[MAX_SKIPLIST_DEPTH+1];\n+\tstruct skiplist *list = &priv_timer[tim_lcore].pending_lists[lcore_id];\n \n \t/* if timer needs to be scheduled on another core, we need to\n \t * lock the list; if it is on local core, we need to lock if\n \t * we are not called from rte_timer_manage() */\n \tif (tim_lcore != lcore_id || !local_is_locked)\n-\t\trte_spinlock_lock(&priv_timer[tim_lcore].list_lock);\n+\t\trte_spinlock_lock(&list->lock);\n \n \t/* find where exactly this element goes in the list of elements\n \t * for each depth. */\n-\ttimer_get_prev_entries(tim->expire, tim_lcore, prev);\n+\ttimer_get_prev_entries(tim->expire, list, prev);\n \n \t/* now assign it a new level and add at that level */\n-\tconst unsigned tim_level = timer_get_skiplist_level(\n-\t\t\tpriv_timer[tim_lcore].curr_skiplist_depth);\n-\tif (tim_level == priv_timer[tim_lcore].curr_skiplist_depth)\n-\t\tpriv_timer[tim_lcore].curr_skiplist_depth++;\n+\tconst unsigned tim_level = timer_get_skiplist_level(list->depth);\n+\tif (tim_level == list->depth)\n+\t\tlist->depth++;\n \n \tlvl = tim_level;\n-\twhile (lvl > 0) {\n+\twhile (lvl > 0 && lvl < MAX_SKIPLIST_DEPTH + 1) {\n \t\ttim->sl_next[lvl] = prev[lvl]->sl_next[lvl];\n \t\tprev[lvl]->sl_next[lvl] = tim;\n \t\tlvl--;\n@@ -310,11 +331,10 @@ timer_add(struct rte_timer *tim, unsigned tim_lcore, int local_is_locked)\n \n \t/* save the lowest list entry into the expire field of the dummy hdr\n \t * NOTE: this is not atomic on 32-bit*/\n-\tpriv_timer[tim_lcore].pending_head.expire = priv_timer[tim_lcore].\\\n-\t\t\tpending_head.sl_next[0]->expire;\n+\tlist->head.expire = list->head.sl_next[0]->expire;\n \n \tif (tim_lcore != lcore_id || !local_is_locked)\n-\t\trte_spinlock_unlock(&priv_timer[tim_lcore].list_lock);\n+\t\trte_spinlock_unlock(&list->lock);\n }\n \n /*\n@@ -330,35 +350,38 @@ timer_del(struct rte_timer *tim, union rte_timer_status prev_status,\n \tunsigned prev_owner = prev_status.owner;\n \tint i;\n \tstruct rte_timer *prev[MAX_SKIPLIST_DEPTH+1];\n+\tstruct skiplist *list;\n+\n+\tlist = &priv_timer[prev_owner].pending_lists[prev_status.installer];\n \n \t/* if timer needs is pending another core, we need to lock the\n \t * list; if it is on local core, we need to lock if we are not\n \t * called from rte_timer_manage() */\n \tif (prev_owner != lcore_id || !local_is_locked)\n-\t\trte_spinlock_lock(&priv_timer[prev_owner].list_lock);\n+\t\trte_spinlock_lock(&list->lock);\n \n \t/* save the lowest list entry into the expire field of the dummy hdr.\n \t * NOTE: this is not atomic on 32-bit */\n-\tif (tim == priv_timer[prev_owner].pending_head.sl_next[0])\n-\t\tpriv_timer[prev_owner].pending_head.expire =\n-\t\t\t\t((tim->sl_next[0] == NULL) ? 0 : tim->sl_next[0]->expire);\n+\tif (tim == list->head.sl_next[0])\n+\t\tlist->head.expire = ((tim->sl_next[0] == NULL) ?\n+\t\t\t\t     0 : tim->sl_next[0]->expire);\n \n \t/* adjust pointers from previous entries to point past this */\n-\ttimer_get_prev_entries_for_node(tim, prev_owner, prev);\n-\tfor (i = priv_timer[prev_owner].curr_skiplist_depth - 1; i >= 0; i--) {\n+\ttimer_get_prev_entries_for_node(tim, list, prev);\n+\tfor (i = list->depth - 1; i >= 0; i--) {\n \t\tif (prev[i]->sl_next[i] == tim)\n \t\t\tprev[i]->sl_next[i] = tim->sl_next[i];\n \t}\n \n \t/* in case we deleted last entry at a level, adjust down max level */\n-\tfor (i = priv_timer[prev_owner].curr_skiplist_depth - 1; i >= 0; i--)\n-\t\tif (priv_timer[prev_owner].pending_head.sl_next[i] == NULL)\n-\t\t\tpriv_timer[prev_owner].curr_skiplist_depth --;\n+\tfor (i = list->depth - 1; i >= 0; i--)\n+\t\tif (list->head.sl_next[i] == NULL)\n+\t\t\tlist->depth--;\n \t\telse\n \t\t\tbreak;\n \n \tif (prev_owner != lcore_id || !local_is_locked)\n-\t\trte_spinlock_unlock(&priv_timer[prev_owner].list_lock);\n+\t\trte_spinlock_unlock(&list->lock);\n }\n \n /* Reset and start the timer associated with the timer handle (private func) */\n@@ -416,7 +439,8 @@ __rte_timer_reset(struct rte_timer *tim, uint64_t expire,\n \t * the state so we don't need to use cmpset() here */\n \trte_wmb();\n \tstatus.state = RTE_TIMER_PENDING;\n-\tstatus.owner = (int16_t)tim_lcore;\n+\tstatus.installer = lcore_id;\n+\tstatus.owner = tim_lcore;\n \ttim->status.u32 = status.u32;\n \n \treturn 0;\n@@ -484,7 +508,8 @@ rte_timer_stop(struct rte_timer *tim)\n \t/* mark timer as stopped */\n \trte_wmb();\n \tstatus.state = RTE_TIMER_STOP;\n-\tstatus.owner = RTE_TIMER_NO_OWNER;\n+\tstatus.installer = RTE_TIMER_NO_LCORE;\n+\tstatus.owner = RTE_TIMER_NO_LCORE;\n \ttim->status.u32 = status.u32;\n \n \treturn 0;\n@@ -506,119 +531,179 @@ rte_timer_pending(struct rte_timer *tim)\n }\n \n /* must be called periodically, run all timer that expired */\n-void rte_timer_manage(void)\n+void\n+rte_timer_manage(void)\n {\n \tunion rte_timer_status status;\n-\tstruct rte_timer *tim, *next_tim;\n-\tstruct rte_timer *run_first_tim, **pprev;\n-\tunsigned lcore_id = rte_lcore_id();\n+\tstruct rte_timer *tim, *next_tim, **pprev;\n+\tstruct rte_timer *run_first_tims[RTE_MAX_LCORE + 1];\n \tstruct rte_timer *prev[MAX_SKIPLIST_DEPTH + 1];\n-\tuint64_t cur_time;\n-\tint i, ret;\n+\tstruct priv_timer *priv_tim;\n+\tunsigned installer_lcore, lcore_id = rte_lcore_id();\n+\tuint64_t cur_time, min_expire;\n+\tint i, j, min_idx, ret;\n+\tint nrunlists = 0;\n+\tint local_is_locked = 0;\n+\tstruct skiplist *dest_list, *list = NULL;\n+\tbool done;\n \n \t/* timer manager only runs on EAL thread with valid lcore_id */\n \tassert(lcore_id < RTE_MAX_LCORE);\n \n+\tpriv_tim = &priv_timer[lcore_id];\n+\n \t__TIMER_STAT_ADD(manage, 1);\n-\t/* optimize for the case where per-cpu list is empty */\n-\tif (priv_timer[lcore_id].pending_head.sl_next[0] == NULL)\n-\t\treturn;\n-\tcur_time = rte_get_timer_cycles();\n+\tfor (i = 0, installer_lcore = enabled_lcores[i]; i < n_enabled_lcores;\n+\t     installer_lcore = enabled_lcores[++i]) {\n+\t\tlist = &priv_tim->pending_lists[installer_lcore];\n+\n+\t\t/* optimize for the case where list is empty */\n+\t\tif (list->head.sl_next[0] == NULL)\n+\t\t\tcontinue;\n+\t\tcur_time = rte_get_timer_cycles();\n \n #ifdef RTE_ARCH_X86_64\n-\t/* on 64-bit the value cached in the pending_head.expired will be\n-\t * updated atomically, so we can consult that for a quick check here\n-\t * outside the lock */\n-\tif (likely(priv_timer[lcore_id].pending_head.expire > cur_time))\n-\t\treturn;\n+\t\t/* on 64-bit the value cached in the list->head.expired will be\n+\t\t * updated atomically, so we can consult that for a quick check\n+\t\t * here outside the lock\n+\t\t */\n+\t\tif (likely(list->head.expire > cur_time))\n+\t\t\tcontinue;\n #endif\n \n-\t/* browse ordered list, add expired timers in 'expired' list */\n-\trte_spinlock_lock(&priv_timer[lcore_id].list_lock);\n+\t\t/* browse ordered list, add expired timers in 'expired' list */\n+\t\trte_spinlock_lock(&list->lock);\n \n-\t/* if nothing to do just unlock and return */\n-\tif (priv_timer[lcore_id].pending_head.sl_next[0] == NULL ||\n-\t    priv_timer[lcore_id].pending_head.sl_next[0]->expire > cur_time) {\n-\t\trte_spinlock_unlock(&priv_timer[lcore_id].list_lock);\n-\t\treturn;\n-\t}\n+\t\t/* if nothing to do just unlock and continue */\n+\t\tif (list->head.sl_next[0] == NULL ||\n+\t\t    list->head.sl_next[0]->expire > cur_time) {\n+\t\t\trte_spinlock_unlock(&list->lock);\n+\t\t\tcontinue;\n+\t\t}\n \n-\t/* save start of list of expired timers */\n-\ttim = priv_timer[lcore_id].pending_head.sl_next[0];\n+\t\t/* save start of list of expired timers */\n+\t\ttim = list->head.sl_next[0];\n+\n+\t\t/* break the existing list at current time point */\n+\t\ttimer_get_prev_entries(cur_time, list, prev);\n+\t\tfor (j = list->depth - 1; j >= 0; j--) {\n+\t\t\tif (prev[j] == &list->head)\n+\t\t\t\tcontinue;\n+\t\t\tlist->head.sl_next[j] =\n+\t\t\t    prev[j]->sl_next[j];\n+\t\t\tif (prev[j]->sl_next[j] == NULL)\n+\t\t\t\tlist->depth--;\n+\t\t\tprev[j]->sl_next[j] = NULL;\n+\t\t}\n \n-\t/* break the existing list at current time point */\n-\ttimer_get_prev_entries(cur_time, lcore_id, prev);\n-\tfor (i = priv_timer[lcore_id].curr_skiplist_depth -1; i >= 0; i--) {\n-\t\tif (prev[i] == &priv_timer[lcore_id].pending_head)\n-\t\t\tcontinue;\n-\t\tpriv_timer[lcore_id].pending_head.sl_next[i] =\n-\t\t    prev[i]->sl_next[i];\n-\t\tif (prev[i]->sl_next[i] == NULL)\n-\t\t\tpriv_timer[lcore_id].curr_skiplist_depth--;\n-\t\tprev[i] ->sl_next[i] = NULL;\n-\t}\n+\t\t/* transition run-list from PENDING to RUNNING */\n+\t\trun_first_tims[nrunlists] = tim;\n+\t\tpprev = &run_first_tims[nrunlists];\n+\t\tnrunlists++;\n+\n+\t\tfor (; tim != NULL; tim = next_tim) {\n+\t\t\tnext_tim = tim->sl_next[0];\n+\n+\t\t\tret = timer_set_running_state(tim);\n+\t\t\tif (likely(ret == 0)) {\n+\t\t\t\tpprev = &tim->sl_next[0];\n+\t\t\t} else {\n+\t\t\t\t/* another core is trying to re-config this one,\n+\t\t\t\t * remove it from local expired list\n+\t\t\t\t */\n+\t\t\t\t*pprev = next_tim;\n+\t\t\t}\n+\t\t}\n \n-\t/* transition run-list from PENDING to RUNNING */\n-\trun_first_tim = tim;\n-\tpprev = &run_first_tim;\n+\t\t/* update the next to expire timer value */\n+\t\tlist->head.expire = (list->head.sl_next[0] == NULL) ?\n+\t\t\t\t    0 : list->head.sl_next[0]->expire;\n \n-\tfor ( ; tim != NULL; tim = next_tim) {\n-\t\tnext_tim = tim->sl_next[0];\n+\t\trte_spinlock_unlock(&list->lock);\n+\t}\n \n-\t\tret = timer_set_running_state(tim);\n-\t\tif (likely(ret == 0)) {\n-\t\t\tpprev = &tim->sl_next[0];\n-\t\t} else {\n-\t\t\t/* another core is trying to re-config this one,\n-\t\t\t * remove it from local expired list\n-\t\t\t */\n-\t\t\t*pprev = next_tim;\n+\t/* Now process the run lists */\n+\twhile (1) {\n+\t\tdone = true;\n+\t\tmin_expire = UINT64_MAX;\n+\t\tmin_idx = 0;\n+\n+\t\t/* Find the next oldest timer to process */\n+\t\tfor (i = 0; i < nrunlists; i++) {\n+\t\t\ttim = run_first_tims[i];\n+\n+\t\t\tif (tim != NULL && tim->expire < min_expire) {\n+\t\t\t\tmin_expire = tim->expire;\n+\t\t\t\tmin_idx = i;\n+\t\t\t\tdone = false;\n+\t\t\t}\n \t\t}\n-\t}\n \n-\t/* update the next to expire timer value */\n-\tpriv_timer[lcore_id].pending_head.expire =\n-\t    (priv_timer[lcore_id].pending_head.sl_next[0] == NULL) ? 0 :\n-\t\tpriv_timer[lcore_id].pending_head.sl_next[0]->expire;\n+\t\tif (done)\n+\t\t\tbreak;\n+\n+\t\ttim = run_first_tims[min_idx];\n \n-\trte_spinlock_unlock(&priv_timer[lcore_id].list_lock);\n+\t\t/* Move down the runlist from which we picked a timer to\n+\t\t * execute\n+\t\t */\n+\t\trun_first_tims[min_idx] = run_first_tims[min_idx]->sl_next[0];\n \n-\t/* now scan expired list and call callbacks */\n-\tfor (tim = run_first_tim; tim != NULL; tim = next_tim) {\n-\t\tnext_tim = tim->sl_next[0];\n-\t\tpriv_timer[lcore_id].updated = 0;\n-\t\tpriv_timer[lcore_id].running_tim = tim;\n+\t\tpriv_tim->updated = 0;\n+\t\tpriv_tim->running_tim = tim;\n \n \t\t/* execute callback function with list unlocked */\n \t\ttim->f(tim, tim->arg);\n \n \t\t__TIMER_STAT_ADD(pending, -1);\n \t\t/* the timer was stopped or reloaded by the callback\n-\t\t * function, we have nothing to do here */\n-\t\tif (priv_timer[lcore_id].updated == 1)\n+\t\t * function, we have nothing to do here\n+\t\t */\n+\t\tif (priv_tim->updated == 1)\n \t\t\tcontinue;\n \n \t\tif (tim->period == 0) {\n \t\t\t/* remove from done list and mark timer as stopped */\n \t\t\tstatus.state = RTE_TIMER_STOP;\n-\t\t\tstatus.owner = RTE_TIMER_NO_OWNER;\n+\t\t\tstatus.installer = RTE_TIMER_NO_LCORE;\n+\t\t\tstatus.owner = RTE_TIMER_NO_LCORE;\n \t\t\trte_wmb();\n \t\t\ttim->status.u32 = status.u32;\n \t\t}\n \t\telse {\n-\t\t\t/* keep it in list and mark timer as pending */\n-\t\t\trte_spinlock_lock(&priv_timer[lcore_id].list_lock);\n+\t\t\tdest_list = &priv_tim->pending_lists[lcore_id];\n+\n+\t\t\t/* If the destination list is the current list\n+\t\t\t * we can acquire the lock here, and hold it\n+\t\t\t * across removal and insertion of the timer.\n+\t\t\t */\n+\t\t\tif (list == dest_list) {\n+\t\t\t\trte_spinlock_lock(&list->lock);\n+\t\t\t\tlocal_is_locked = 1;\n+\t\t\t}\n+\n+\t\t\t/* set timer state back to PENDING and\n+\t\t\t * reinsert it in pending list\n+\t\t\t */\n \t\t\tstatus.state = RTE_TIMER_PENDING;\n \t\t\t__TIMER_STAT_ADD(pending, 1);\n-\t\t\tstatus.owner = (int16_t)lcore_id;\n+\t\t\tstatus.installer = tim->status.installer;\n+\t\t\tstatus.owner = lcore_id;\n \t\t\trte_wmb();\n \t\t\ttim->status.u32 = status.u32;\n-\t\t\t__rte_timer_reset(tim, tim->expire + tim->period,\n-\t\t\t\ttim->period, lcore_id, tim->f, tim->arg, 1);\n-\t\t\trte_spinlock_unlock(&priv_timer[lcore_id].list_lock);\n+\n+\t\t\t__rte_timer_reset(tim,\n+\t\t\t\t\t  tim->expire + tim->period,\n+\t\t\t\t\t  tim->period, lcore_id,\n+\t\t\t\t\t  tim->f, tim->arg, local_is_locked);\n+\n+\t\t\tif (local_is_locked) {\n+\t\t\t\trte_spinlock_unlock(&list->lock);\n+\t\t\t\tlocal_is_locked = 0;\n+\t\t\t}\n \t\t}\n \t}\n-\tpriv_timer[lcore_id].running_tim = NULL;\n+\tpriv_tim->running_tim = NULL;\n }\n \n /* dump statistics about timers */\ndiff --git a/lib/librte_timer/rte_timer.h b/lib/librte_timer/rte_timer.h\nindex a276a73..4cc6baf 100644\n--- a/lib/librte_timer/rte_timer.h\n+++ b/lib/librte_timer/rte_timer.h\n@@ -77,7 +77,7 @@ extern \"C\" {\n #define RTE_TIMER_RUNNING 2 /**< State: timer function is running. */\n #define RTE_TIMER_CONFIG  3 /**< State: timer is being configured. */\n \n-#define RTE_TIMER_NO_OWNER -2 /**< Timer has no owner. */\n+#define RTE_TIMER_NO_LCORE -2\n \n /**\n  * Timer type: Periodic or single (one-shot).\n@@ -94,10 +94,11 @@ enum rte_timer_type {\n union rte_timer_status {\n \tRTE_STD_C11\n \tstruct {\n-\t\tuint16_t state;  /**< Stop, pending, running, config. */\n-\t\tint16_t owner;   /**< The lcore that owns the timer. */\n+\t\tunsigned state\t: 2;\n+\t\tint installer\t: 15;\n+\t\tint owner\t: 15;\n \t};\n-\tuint32_t u32;            /**< To atomic-set status + owner. */\n+\tuint32_t u32;            /**< To atomic-set status, installer, owner */\n };\n \n #ifdef RTE_LIBRTE_TIMER_DEBUG\n",
    "prefixes": [
        "dpdk-dev",
        "1/3"
    ]
}