aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/pg
AgeCommit message (Collapse)AuthorFilesLines
2017-11-21dpdk: add l2_hdr_offset and l3_hdr_offset in vlib_buffer_tDamjan Marion2-3/+3
Change-Id: I0a6d1257e391c3b6f7da6498bd5f7d4c545d17e9 Signed-off-by: Damjan Marion <damarion@cisco.com>
2017-11-10Break up vpe.apiNeale Ranns2-0/+269
- makes the VAPI generated file more consumable. - VOM build times improve. Change-Id: I838488930bd23a0d3818adfdffdbca3eead382df Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
2017-10-04[aarch64] Fixes CLI crashes on dpaa2 platform.Christophe Fontaine2-2/+2
- always use 'va_args' as pointer in all format_* functions - u32 for all 'indent' params as it's declaration was inconsistent Change-Id: Ic5799309a6b104c9b50fec309cba789c8da99e79 Signed-off-by: Christophe Fontaine <christophe.fontaine@enea.com>
2017-07-10vlib: store buffer memory information in the buffer_mainDamjan Marion2-4/+4
Currently, buffer index is calculated as a offset to the physmem region shifted by log2_cacheline size. When DPDK is used we "hack" physmem data with information taken from dpdk mempool. This makes physmem code not usable with DPDK. This change makes buffer memory start and size independent of physmem basically allowing physmem to be used when DPDK plugin is loaded. Change-Id: Ieb399d398f147583b9baab467152a352d58c9c31 Signed-off-by: Damjan Marion <damarion@cisco.com>
2017-04-06Use thread local storage for thread indexDamjan Marion1-2/+2
This patch deprecates stack-based thread identification, Also removes requirement that thread stacks are adjacent. Finally, possibly annoying for some folks, it renames all occurences of cpu_index and cpu_number with thread index. Using word "cpu" is misleading here as thread can be migrated ti different CPU, and also it is not related to linux cpu index. Change-Id: I68cdaf661e701d2336fc953dcb9978d10a70f7c1 Signed-off-by: Damjan Marion <damarion@cisco.com>
2017-03-17Fix IP feature ordering.Neale Ranns1-4/+0
Drop comes before lookup when enabled. is_first_or_last is not required when setting a feature, the anchor is added in find_config_with_features(). Don't make the PG interfaces automatically L3 enabled, this way we can have tests that check the L3 protocol disbaled behaviour. Change-Id: Icef22a920b27ff9cec6ab2da6b05f05c532cb60f Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-03-07DHCP Multiple Servers (VPP-602, VPP-605)Neale Ranns1-0/+6
Multiple DHCP (4 and/or 6) servers can be added and removed through multiple calls to the 'set dhcp server' API. All 4/6/ discover/solicit messages will then be replicated to all servers in the list. The expectation is that the servers/system is configured in such a way that this is viable. If VSS information is providied for the clinet VRF which also has multiple servers configured, then the same VSS information is sent to each server. Likewise the source address of packets sent to from VPP to each server is the same. Change-Id: I3287cb084c84b3f612b78bc69cfcb5b9c1f8934d Signed-off-by: Neale Ranns <nranns@cisco.com>
2017-03-01dpdk: be a pluginDamjan Marion2-8/+8
Change-Id: I238258cdeb77035adc5e88903d824593d0a1da90 Signed-off-by: Damjan Marion <damarion@cisco.com>
2017-02-22VPP-635: CLI Memory leak with invalid parameterBilly McFall1-10/+29
In the CLI parsing, below is a common pattern: /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return 0; while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { if (unformat (line_input, "x")) x = 1; : else return clib_error_return (0, "unknown input `%U'", format_unformat_error, line_input); } unformat_free (line_input); The 'else' returns if an unknown string is encountered. There a memory leak because the 'unformat_free(line_input)' is not called. There is a large number of instances of this pattern. Replaced the previous pattern with: /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return 0; while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { if (unformat (line_input, "x")) x = 1; : else { error = clib_error_return (0, "unknown input `%U'", format_unformat_error, line_input); goto done: } } /* ...Remaining code... */ done: unformat_free (line_input); return error; } In multiple files, 'unformat_free (line_input);' was never called, so there was a memory leak whether an invalid string was entered or not. Also, there were multiple instance where: error = clib_error_return (0, "unknown input `%U'", format_unformat_error, line_input); used 'input' as the last parameter instead of 'line_input'. The result is that output did not contain the substring in error, instead just an empty string. Fixed all of those as well. There are a lot of file, and very mind numbing work, so tried to keep it to a pattern to avoid mistakes. Change-Id: I8902f0c32a47dd7fb3bb3471a89818571702f1d2 Signed-off-by: Billy McFall <bmcfall@redhat.com> Signed-off-by: Dave Barach <dave@barachs.net>
2017-02-03pg: add trace information for pg tx nodeDamjan Marion3-9/+46
Change-Id: I31730d58c34331f25f5b02cd065be94251f1302c Signed-off-by: Damjan Marion <damarion@cisco.com>
2016-12-28Reorganize source tree to use single autotools instanceDamjan Marion9-0/+3742
Change-Id: I7b51f88292e057c6443b12224486f2d0c9f8ae23 Signed-off-by: Damjan Marion <damarion@cisco.com>
or: #75715e } /* Comment.Hashbang */ .highlight .cm { color: #75715e } /* Comment.Multiline */ .highlight .cp { color: #75715e } /* Comment.Preproc */ .highlight .cpf { color: #75715e } /* Comment.PreprocFile */ .highlight .c1 { color: #75715e } /* Comment.Single */ .highlight .cs { color: #75715e } /* Comment.Special */ .highlight .gd { color: #f92672 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gi { color: #a6e22e } /* Generic.Inserted */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #75715e } /* Generic.Subheading */ .highlight .kc { color: #66d9ef } /* Keyword.Constant */ .highlight .kd { color: #66d9ef } /* Keyword.Declaration */ .highlight .kn { color: #f92672 } /* Keyword.Namespace */ .highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ .highlight .kr { color: #66d9ef } /* Keyword.Reserved */ .highlight .kt { color: #66d9ef } /* Keyword.Type */ .highlight .ld { color: #e6db74 } /* Literal.Date */ .highlight .m { color: #ae81ff } /* Literal.Number */ .highlight .s { color: #e6db74 } /* Literal.String */ .highlight .na { color: #a6e22e } /* Name.Attribute */ .highlight .nb { color: #f8f8f2 } /* Name.Builtin */ .highlight .nc { color: #a6e22e } /* Name.Class */ .highlight .no { color: #66d9ef } /* Name.Constant */ .highlight .nd { color: #a6e22e } /* Name.Decorator */ .highlight .ni { color: #f8f8f2 } /* Name.Entity */ .highlight .ne { color: #a6e22e } /* Name.Exception */ .highlight .nf { color: #a6e22e } /* Name.Function */ .highlight .nl { color: #f8f8f2 } /* Name.Label */ .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ .highlight .nx { color: #a6e22e } /* Name.Other */ .highlight .py { color: #f8f8f2 } /* Name.Property */ .highlight .nt { color: #f92672 } /* Name.Tag */ .highlight .nv { color: #f8f8f2 } /* Name.Variable */ .highlight .ow { color: #f92672 } /* Operator.Word */ .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ .highlight .sa { color: #e6db74 } /* Literal.String.Affix */ .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ .highlight .sc { color: #e6db74 } /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
/*
 * 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.
 */
/**
 * bier_disposition : The BIER disposition object
 *
 * A BIER disposition object is present in the IP mcast output list
 * and represents the disposition of a BIER bitmask. After BIER header
 * disposition the packet is forward within the appropriate/specified
 * BIER table
 */

#include <vnet/bier/bier_disp_entry.h>
#include <vnet/bier/bier_hdr_inlines.h>
#include <vnet/fib/fib_path_list.h>
#include <vnet/dpo/drop_dpo.h>

/**
 * The memory pool of all imp objects
 */
bier_disp_entry_t *bier_disp_entry_pool;

/**
 * When constructing the BIER imp ID from an index and BSL, shift
 * the BSL this far
 */
#define BIER_DISP_ENTRY_ID_HLEN_SHIFT 24

static void
bier_disp_entry_lock_i (bier_disp_entry_t *bde)
{
    bde->bde_locks++;
}

void
bier_disp_entry_lock (index_t bdei)
{
    bier_disp_entry_lock_i(bier_disp_entry_get(bdei));
}

static index_t
bier_disp_entry_get_index(bier_disp_entry_t *bde)
{
    return (bde - bier_disp_entry_pool);
}

index_t
bier_disp_entry_add_or_lock (void)
{
    dpo_id_t invalid = DPO_INVALID;
    bier_hdr_proto_id_t pproto;
    bier_disp_entry_t *bde;

    pool_get_aligned(bier_disp_entry_pool, bde, CLIB_CACHE_LINE_BYTES);

    bde->bde_locks = 0;

    FOR_EACH_BIER_HDR_PROTO(pproto)
    {
        bde->bde_fwd[pproto].bde_dpo = invalid;
        bde->bde_fwd[pproto].bde_rpf_id = ~0;
        bde->bde_pl[pproto] = FIB_NODE_INDEX_INVALID;
    }

    bier_disp_entry_lock_i(bde);
    return (bier_disp_entry_get_index(bde));
}

void
bier_disp_entry_unlock (index_t bdei)
{
    bier_disp_entry_t *bde;

    if (INDEX_INVALID == bdei)
    {
        return;
    }

    bde = bier_disp_entry_get(bdei);

    bde->bde_locks--;

    if (0 == bde->bde_locks)
    {
        bier_hdr_proto_id_t pproto;

        FOR_EACH_BIER_HDR_PROTO(pproto)
        {
            dpo_unlock(&bde->bde_fwd[pproto].bde_dpo);
            bde->bde_fwd[pproto].bde_rpf_id = ~0;
            fib_path_list_unlock(bde->bde_pl[pproto]);
        }
        pool_put(bier_disp_entry_pool, bde);
    }
}

typedef struct bier_disp_entry_path_list_walk_ctx_t_
{
    u32 bdew_rpf_id;
} bier_disp_entry_path_list_walk_ctx_t;

static fib_path_list_walk_rc_t
bier_disp_entry_path_list_walk (fib_node_index_t pl_index,
                                fib_node_index_t path_index,
                                void *arg)
{
    bier_disp_entry_path_list_walk_ctx_t *ctx = arg;

    ctx->bdew_rpf_id = fib_path_get_rpf_id(path_index);

    if (~0 != ctx->bdew_rpf_id)
    {
        return (FIB_PATH_LIST_WALK_STOP);
    }
    return (FIB_PATH_LIST_WALK_CONTINUE);
}

static void
bier_disp_entry_restack (bier_disp_entry_t *bde,
                         bier_hdr_proto_id_t pproto)
{
    dpo_id_t via_dpo = DPO_INVALID;
    fib_node_index_t pli;

    pli = bde->bde_pl[pproto];

    if (FIB_NODE_INDEX_INVALID == pli)
    {
        dpo_copy(&via_dpo,
                 drop_dpo_get(bier_hdr_proto_to_dpo(pproto)));
    }
    else
    {
        fib_path_list_contribute_forwarding(pli,
                                            fib_forw_chain_type_from_dpo_proto(
                                                bier_hdr_proto_to_dpo(pproto)),
                                            FIB_PATH_LIST_FWD_FLAG_COLLAPSE,
                                            &via_dpo);

        bier_disp_entry_path_list_walk_ctx_t ctx = {
            .bdew_rpf_id = ~0,
        };

        fib_path_list_walk(pli, bier_disp_entry_path_list_walk, &ctx);
        bde->bde_fwd[pproto].bde_rpf_id = ctx.bdew_rpf_id;
    }

    dpo_stack(DPO_BIER_DISP_ENTRY,
              DPO_PROTO_BIER,
              &bde->bde_fwd[pproto].bde_dpo,
              &via_dpo);
}

void
bier_disp_entry_path_add (index_t bdei,
                          bier_hdr_proto_id_t pproto,
                          const fib_route_path_t *rpaths)
{
    fib_node_index_t *pli, old_pli;
    bier_disp_entry_t *bde;

    bde = bier_disp_entry_get(bdei);
    pli = &bde->bde_pl[pproto];
    old_pli = *pli;

    /*
     * create a new or update the existing path-list for this
     * payload protocol
     */
    if (FIB_NODE_INDEX_INVALID == *pli)
    {
        *pli = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED |
                                     FIB_PATH_LIST_FLAG_NO_URPF),
                                    rpaths);
    }
    else
    {
        *pli = fib_path_list_copy_and_path_add(old_pli,
                                               (FIB_PATH_LIST_FLAG_SHARED |
                                                FIB_PATH_LIST_FLAG_NO_URPF),
                                               rpaths);
    }

    fib_path_list_lock(*pli);
    fib_path_list_unlock(old_pli);

    bier_disp_entry_restack(bde, pproto);
}

int
bier_disp_entry_path_remove (index_t bdei,
                             bier_hdr_proto_id_t pproto,
                             const fib_route_path_t *rpaths)
{
    fib_node_index_t *pli, old_pli;
    bier_disp_entry_t *bde;

    bde = bier_disp_entry_get(bdei);
    pli = &bde->bde_pl[pproto];
    old_pli = *pli;

    /*
     * update the existing path-list for this payload protocol
     */
    if (FIB_NODE_INDEX_INVALID != *pli)
    {
        *pli = fib_path_list_copy_and_path_remove(old_pli,
                                                  (FIB_PATH_LIST_FLAG_SHARED |
                                                   FIB_PATH_LIST_FLAG_NO_URPF),
                                                  rpaths);

        fib_path_list_lock(*pli);
        fib_path_list_unlock(old_pli);

        bier_disp_entry_restack(bde, pproto);
    }

    /*
     * if there are no path-list defined for any payload protocol
     * then this entry is OK for removal
     */
    int remove = 1;

    FOR_EACH_BIER_HDR_PROTO(pproto)
    {
        if (FIB_NODE_INDEX_INVALID != bde->bde_pl[pproto])
        {
            remove = 0;
            break;
        }
    }

    return (remove);
}

u8*
format_bier_disp_entry (u8* s, va_list *args)
{
    index_t bdei = va_arg (*args, index_t);
    u32 indent = va_arg(*args, u32);
    bier_show_flags_t flags = va_arg(*args, bier_show_flags_t);
    bier_hdr_proto_id_t pproto;
    bier_disp_entry_t *bde;

    bde = bier_disp_entry_get(bdei);

    s = format(s, "%Ubier-disp:[%d]", format_white_space, indent, bdei);

    FOR_EACH_BIER_HDR_PROTO(pproto)
    {
        if (INDEX_INVALID != bde->bde_pl[pproto])
        {
            s = format(s, "\n%U%U\n",
                       format_white_space, indent+2,
                       format_bier_hdr_proto, pproto);
            s = format(s, "%U", format_fib_path_list, bde->bde_pl[pproto], indent+4);

            if (flags & BIER_SHOW_DETAIL)
            {
                s = format(s, "\n%UForwarding:",
                           format_white_space, indent+4);
                s = format(s, "\n%Urpf-id:%d",
                           format_white_space, indent+6,
                           bde->bde_fwd[pproto].bde_rpf_id);
                s = format(s, "\n%U%U",
                           format_white_space, indent+6,
                           format_dpo_id, &bde->bde_fwd[pproto].bde_dpo, indent+2);
            }
        }
    }
    return (s);
}

void
bier_disp_entry_contribute_forwarding (index_t bdei,
                                       dpo_id_t *dpo)
{
    dpo_set(dpo, DPO_BIER_DISP_ENTRY, DPO_PROTO_BIER, bdei);
}

const static char* const bier_disp_entry_bier_nodes[] =
{
    "bier-disp-dispatch",
    NULL,
};

const static char* const * const bier_disp_entry_nodes[DPO_PROTO_NUM] =
{
    [DPO_PROTO_BIER]  = bier_disp_entry_bier_nodes,
};

static void
bier_disp_entry_dpo_lock (dpo_id_t *dpo)
{
    bier_disp_entry_lock(dpo->dpoi_index);
}

static void
bier_disp_entry_dpo_unlock (dpo_id_t *dpo)
{
    bier_disp_entry_unlock(dpo->dpoi_index);
}

static void
bier_disp_entry_dpo_mem_show (void)
{
    fib_show_memory_usage("BIER disposition",
                          pool_elts(bier_disp_entry_pool),
                          pool_len(bier_disp_entry_pool),
                          sizeof(bier_disp_entry_t));
}

static u8*
format_bier_disp_entry_dpo (u8* s, va_list *ap)
{
    index_t index = va_arg(*ap, index_t);
    u32 indent = va_arg(*ap, u32);

    s = format(s, "%U", format_bier_disp_entry, index, indent, BIER_SHOW_DETAIL);

    return (s);
}

const static dpo_vft_t bier_disp_entry_vft = {
    .dv_lock = bier_disp_entry_dpo_lock,
    .dv_unlock = bier_disp_entry_dpo_unlock,
    .dv_format = format_bier_disp_entry_dpo,
    .dv_mem_show = bier_disp_entry_dpo_mem_show,
};

clib_error_t *
bier_disp_entry_db_module_init (vlib_main_t *vm)
{
    dpo_register(DPO_BIER_DISP_ENTRY,
                 &bier_disp_entry_vft,
                 bier_disp_entry_nodes);

    return (NULL);
}

VLIB_INIT_FUNCTION (bier_disp_entry_db_module_init);

static clib_error_t *
show_bier_disp_entry (vlib_main_t * vm,
               unformat_input_t * input,
               vlib_cli_command_t * cmd)
{
    index_t bdei;

    bdei = INDEX_INVALID;

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
        if (unformat (input, "%d", &bdei))
            ;
        else
        {
            break;
        }
    }

    if (INDEX_INVALID == bdei)
    {
        return (NULL);
    }
    else
    {
        if (pool_is_free_index(bier_disp_entry_pool, bdei))
        {
            vlib_cli_output(vm, "No such BIER disp entry: %d", bdei);
        }
        else
        {
            vlib_cli_output(vm, "%U", format_bier_disp_entry, bdei, 1,
                            BIER_SHOW_DETAIL);
        }
    }
    return (NULL);
}

VLIB_CLI_COMMAND (show_bier_disp_entry_node, static) = {
    .path = "show bier disp entry",
    .short_help = "show bier disp entry index",
    .function = show_bier_disp_entry,
};