/*
 * BSD LICENSE
 *
 * Copyright(c) 2014-2020 Intel Corporation. All rights reserved.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @brief  Set of utility functions to list and retrieve L3CA setting profiles
 */
#include <stdio.h>
#include <string.h>

#include "profiles.h"
#include "pqos.h"
#include "main.h"
#include "alloc.h"

#define PROFILES_MIN_COS 4

/**
 * 11-cache ways
 */
static const char * const classes_way11_overlapN_equalY[] = {
        "0=0x007",
        "1=0x038",
        "2=0x1C0",
        "3=0x600"
};

static const char * const classes_way11_overlapN_equalN[] = {
        "0=0x01F",
        "1=0x060",
        "2=0x180",
        "3=0x600"
};

static const char * const classes_way11_overlapP0_equalN[] = {
        "0=0x7FF",
        "1=0x060",
        "2=0x180",
        "3=0x600"
};

static const char * const classes_way11_overlapY_equalN[] = {
        "0=0x7FF",
        "1=0x7F0",
        "2=0x700",
        "3=0x600"
};

/**
 * 12-cache ways
 */
static const char * const classes_way12_overlapN_equalY[] = {
        "0=0x007",
        "1=0x038",
        "2=0x1C0",
        "3=0xE00"
};

static const char * const classes_way12_overlapN_equalN[] = {
        "0=0x03F",
        "1=0x0C0",
        "2=0x300",
        "3=0xC00"
};

static const char * const classes_way12_overlapP0_equalN[] = {
        "0=0xFFF",
        "1=0x0C0",
        "2=0x300",
        "3=0xC00"
};

static const char * const classes_way12_overlapY_equalN[] = {
        "0=0xFFF",
        "1=0xFF0",
        "2=0xF00",
        "3=0xC00"
};

/**
 * 16-cache ways
 */
static const char * const classes_way16_overlapN_equalY[] = {
        "0=0x000F",
        "1=0x00F0",
        "2=0x0F00",
        "3=0xF000"
};

static const char * const classes_way16_overlapN_equalN[] = {
        "0=0x03FF",
        "1=0x0C00",
        "2=0x3000",
        "3=0xC000"
};

static const char * const classes_way16_overlapP0_equalN[] = {
        "0=0xFFFF",
        "1=0x0C00",
        "2=0x3000",
        "3=0xC000"
};

static const char * const classes_way16_overlapY_equalN[] = {
        "0=0xFFFF",
        "1=0xFF00",
        "2=0xF000",
        "3=0xC000"
};

/**
 * 20-cache ways
 */
static const char * const classes_way20_overlapN_equalY[] = {
        "0=0x0001F",
        "1=0x003E0",
        "2=0x07C00",
        "3=0xF8000"
};

static const char * const classes_way20_overlapN_equalN[] = {
        "0=0x000FF",
        "1=0x00F00",
        "2=0x0F000",
        "3=0xF000"
};

static const char * const classes_way20_overlapP0_equalN[] = {
        "0=0xFFFFF",
        "1=0x0C000",
        "2=0x30000",
        "3=0xC0000"
};

static const char * const classes_way20_overlapY_equalN[] = {
        "0=0xFFFFF",
        "1=0xFF000",
        "2=0xF0000",
        "3=0xC0000"
};

/**
 * meat and potatoes now :)
 */
struct llc_allocation_config {
        unsigned num_ways;
        unsigned num_classes;
        const char * const *tab;
};

static const struct llc_allocation_config config_cfg0[] = {
        { .num_ways = 11,
          .num_classes = 4,
          .tab = classes_way11_overlapN_equalY,
        },
        { .num_ways = 12,
          .num_classes = 4,
          .tab = classes_way12_overlapN_equalY,
        },
        { .num_ways = 16,
          .num_classes = 4,
          .tab = classes_way16_overlapN_equalY,
        },
        { .num_ways = 20,
          .num_classes = 4,
          .tab = classes_way20_overlapN_equalY,
        }
};

static const struct llc_allocation_config config_cfg1[] = {
        { .num_ways = 11,
          .num_classes = 4,
          .tab = classes_way11_overlapN_equalN,
        },
        { .num_ways = 12,
          .num_classes = 4,
          .tab = classes_way12_overlapN_equalN,
        },
        { .num_ways = 16,
          .num_classes = 4,
          .tab = classes_way16_overlapN_equalN,
        },
        { .num_ways = 20,
          .num_classes = 4,
          .tab = classes_way20_overlapN_equalN,
        }
};

static const struct llc_allocation_config config_cfg2[] = {
        { .num_ways = 11,
          .num_classes = 4,
          .tab = classes_way11_overlapP0_equalN,
        },
        { .num_ways = 12,
          .num_classes = 4,
          .tab = classes_way12_overlapP0_equalN,
        },
        { .num_ways = 16,
          .num_classes = 4,
          .tab = classes_way16_overlapP0_equalN,
        },
        { .num_ways = 20,
          .num_classes = 4,
          .tab = classes_way20_overlapP0_equalN,
        },
};

static const struct llc_allocation_config config_cfg3[] = {
        { .num_ways = 11,
          .num_classes = 4,
          .tab = classes_way11_overlapY_equalN,
        },
        { .num_ways = 12,
          .num_classes = 4,
          .tab = classes_way12_overlapY_equalN,
        },
        { .num_ways = 16,
          .num_classes = 4,
          .tab = classes_way16_overlapY_equalN,
        },
        { .num_ways = 20,
          .num_classes = 4,
          .tab = classes_way20_overlapY_equalN,
        },
};

struct llc_allocation {
        const char *id;
        const char *descr;
        unsigned num_config;
        const struct llc_allocation_config *config;
};

static const struct llc_allocation allocation_tab[] = {
        { .id = "CFG0",
          .descr = "non-overlapping, ways equally divided",
          .num_config = DIM(config_cfg0),
          .config = config_cfg0,
        },
        { .id = "CFG1",
          .descr = "non-overlapping, ways unequally divided",
          .num_config = DIM(config_cfg1),
          .config = config_cfg1,
        },
        { .id = "CFG2",
          .descr = "overlapping, ways unequally divided, "
          "class 0 can access all ways",
          .num_config = DIM(config_cfg2),
          .config = config_cfg2,
        },
        { .id = "CFG3",
          .descr = "ways unequally divided, overlapping access "
          "for higher classes",
          .num_config = DIM(config_cfg3),
          .config = config_cfg3,
        },
};

void profile_l3ca_list(void)
{
        unsigned i = 0;

        for (i = 0; i < DIM(allocation_tab); i++) {
                const struct llc_allocation *ap = &allocation_tab[i];
                unsigned j;

                printf("%u)\n"
                       "      Config ID: %s\n"
                       "    Description: %s\n"
                       " Configurations:\n",
                       i+1, ap->id, ap->descr);
                for (j = 0; j < ap->num_config; j++) {
                        printf("\tnumber of classes = %u,"
                               " number of cache ways = %u\n",
                               (unsigned) ap->config[j].num_classes,
                               (unsigned) ap->config[j].num_ways);
                }
        }
}

/**
 * @brief Retrieves selected L3CA profile by its \a id
 *
 * @param [in] id profile identity (string)
 * @param [in] l3ca L3CA capability structure
 * @param [out] p_num number of L3CA classes of service retrieved for
 *              the profile
 * @param [out] p_tab pointer to definition of L3CA classes of service
 *
 * @return Operations status
 * @retval PQOS_RETVAL_OK on success
 */
static int
profile_l3ca_get(const char *id, const struct pqos_cap_l3ca *l3ca,
                 unsigned *p_num, const char * const **p_tab)
{
        unsigned i = 0, j = 0;

        ASSERT(id != NULL);
        ASSERT(l3ca != NULL);
        ASSERT(p_tab != NULL);
        ASSERT(p_num != NULL);

        if (id == NULL || l3ca == NULL || p_tab == NULL || p_num == NULL)
                return PQOS_RETVAL_PARAM;

        if (l3ca->num_classes < PROFILES_MIN_COS)
                return PQOS_RETVAL_RESOURCE;

        for (i = 0; i < DIM(allocation_tab); i++) {
                const struct llc_allocation *ap = &allocation_tab[i];

                if (strcasecmp(id, ap->id) != 0)
                        continue;
                for (j = 0; j < ap->num_config; j++) {
                        /* no need to check number of classes here */
                        if (ap->config[j].num_ways != l3ca->num_ways)
                                continue;
                        *p_num = ap->config[j].num_classes;
                        *p_tab = ap->config[j].tab;
                        return PQOS_RETVAL_OK;
                }
        }

        return PQOS_RETVAL_RESOURCE;
}

int
profile_l3ca_apply(const char *name,
                   const struct pqos_capability *cap_l3ca)
{
        unsigned cnum = 0;
        const char * const *cptr = NULL;

        if (cap_l3ca != NULL &&
            profile_l3ca_get(name, cap_l3ca->u.l3ca, &cnum,
                             &cptr) == PQOS_RETVAL_OK) {
                /**
                 * All profile classes are defined as strings
                 * in format that is command line friendly.
                 *
                 * This effectively simulates series of -e command
                 * line options. "llc:" is glued to each of the strings
                 * so that profile class definitions don't have to
                 * include it.
                 */
                char cb[64];
                unsigned i = 0, offset = 0;

                memset(cb, 0, sizeof(cb));
                strncpy(cb, "llc:", sizeof(cb));
                offset = (unsigned)strlen("llc:");

                for (i = 0; i < cnum; i++) {
                        strncpy(cb+offset, cptr[i],
                                sizeof(cb)-1-offset);
                        selfn_allocation_class(cb);
                }
        } else {
                printf("Allocation profile '%s' not found or "
                       "cache allocation not supported!\n",
                       name);
                return -1;
        }

        return 0;
}
