/*
 * 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.
 */

#ifndef __BIER_TABLE_H__
#define __BIER_TABLE_H__

#include <vlib/vlib.h>
#include <vnet/fib/fib_types.h>
#include <vnet/bier/bier_types.h>
#include <vnet/bier/bier_entry.h>

#include <vnet/dpo/dpo.h>

/**
 * Forward declarations
 */
struct bier_route_update_t_;

/**
 * A BIER Table is the bit-indexed forwarding table.
 * Each entry (bit-position) represents one destination, and its reachability
 *
 * The number of entries in a table is thus the maximum supported
 * bit-position. Since this is small <4096, the table is a flat array
 */
typedef struct bier_table_t_ {
    /**
     * required for pool_get_aligned.
     *  memebers used in the switch path come first!
     */
    CLIB_CACHE_LINE_ALIGN_MARK(cacheline0);

    /**
     * Save the MPLS local label associated with the table
     */
    mpls_label_t bt_ll;

    /**
     * The path-list used for the ECMP-tables
     */
    fib_node_index_t bt_pl;

    /**
     * The index of the lfib entry created for this table.
     * Only the EOS is required.
     */
    fib_node_index_t bt_lfei;

    /**
     * Number of locks on the table
     */
    u16 bt_locks;

    /**
     * Entries in the table
     * This is a vector sized to the appropriate number of entries
     * given the table's supported Bit-string length
     */
    index_t *bt_entries;

    /**
     * The identity/key or the table. we need the hdr_len in the data-path
     */
    bier_table_id_t bt_id;

    /**
     * f-masks in the ECMP table
     * This is a vector sized to the appropriate number of entries
     * given the table's supported Bit-string length.
     * In the ECMP table the LB choice has been pre-resolved, so each entry
     * links to just one f-mask, i.e. there is a 1:1 mapping of bit-position to
     * fmask. For efficient forwarding we collapse the fmasks up to the table.
     */
    index_t *bt_fmasks;
} bier_table_t;

STATIC_ASSERT((sizeof(bier_table_t) <= 2*CLIB_CACHE_LINE_BYTES),
              "BIER table fits on 2 cache lines");

extern index_t bier_table_add_or_lock(const bier_table_id_t *id,
                                      mpls_label_t ll);
extern index_t bier_table_lock(const bier_table_id_t *id);
extern void bier_table_unlock(const bier_table_id_t *id);

extern void bier_table_route_path_add(const bier_table_id_t *bti,
                                      bier_bp_t bp,
                                      fib_route_path_t *brp);
extern void bier_table_route_path_remove(const bier_table_id_t *bti,
                                         bier_bp_t bp,
                                         fib_route_path_t *brp);
extern void bier_table_route_path_update(const bier_table_id_t *bti,
                                         bier_bp_t bp,
                                         fib_route_path_t *brp);
extern void bier_table_route_delete(const bier_table_id_t *bti,
                                    bier_bp_t b);

extern void bier_table_show_all(vlib_main_t * vm,
                                bier_show_flags_t flags);

extern const bier_table_id_t *bier_table_get_id(index_t bti);

extern u8 *format_bier_table (u8 *s, va_list *args);
extern u8 *format_bier_table_entry (u8 *s, va_list *args);

extern index_t bier_table_ecmp_create_and_lock(const bier_table_id_t *id);
extern void bier_table_ecmp_unlock(index_t bti);
extern void bier_table_ecmp_set_fmask(index_t bti,
                                      bier_bp_t bp,
                                      index_t bfmi);

extern void bier_table_contribute_forwarding(index_t bti,
                                             dpo_id_t *dpo);

/**
 * Types and functions to walk the ECMP tables of a main table
 */
typedef void (*bier_table_ecmp_walk_fn_t)(index_t btei,
                                          void *ctx);
extern void bier_table_ecmp_walk(index_t bti,
                                 bier_table_ecmp_walk_fn_t fn,
                                 void *ctx);
extern int bier_table_is_main (const bier_table_t *bt);

/**
 * Types and functions to walk all the BIER Tables
 */
typedef void (*bier_tables_walk_fn_t)(const bier_table_t *bt,
                                      void *ctx);
extern void bier_tables_walk(bier_tables_walk_fn_t fn,
                             void *ctx);

/**
 * Types and functions to walk all the entries in one BIER Table
 */
typedef void (*bier_table_walk_fn_t)(const bier_table_t *bt,
                                     const bier_entry_t *be,
                                     void *ctx);
extern void bier_table_walk(const bier_table_id_t *id,
                            bier_table_walk_fn_t fn,
                            void *ctx);

/*
 * provided for fast data plane access.
 */
extern bier_table_t *bier_table_pool;

static inline bier_table_t *
bier_table_get (index_t bti)
{
    return (pool_elt_at_index(bier_table_pool, bti));
}

static inline const index_t
bier_table_lookup (const bier_table_t *bt,
                   bier_bp_t bp)
{
    return (bt->bt_entries[BIER_BP_TO_INDEX(bp)]);
}

static inline const index_t
bier_table_fwd_lookup (const bier_table_t *bt,
                       bier_bp_t bp)
{
    return (bt->bt_fmasks[BIER_BP_TO_INDEX(bp)]);
}

#endif