// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */
#define dev_fmt(fmt) "RateLimiting: " fmt
#include <asm/errno.h>
#include <asm/div64.h>
#include <linux/dev_printk.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/units.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "adf_rl_admin.h"
#include "adf_rl.h"
#include "adf_sysfs_rl.h"
#define RL_TOKEN_GRANULARITY_PCIEIN_BUCKET 0U
#define RL_TOKEN_GRANULARITY_PCIEOUT_BUCKET 0U
#define RL_TOKEN_PCIE_SIZE 64
#define RL_TOKEN_ASYM_SIZE 1024
#define RL_CSR_SIZE 4U
#define RL_CAPABILITY_MASK GENMASK(6, 4)
#define RL_CAPABILITY_VALUE 0x70
#define RL_VALIDATE_NON_ZERO(input) ((input) == 0)
#define ROOT_MASK GENMASK(1, 0)
#define CLUSTER_MASK GENMASK(3, 0)
#define LEAF_MASK GENMASK(5, 0)
static int validate_user_input(struct adf_accel_dev *accel_dev,
struct adf_rl_sla_input_data *sla_in,
bool is_update)
{
const unsigned long rp_mask = sla_in->rp_mask;
size_t rp_mask_size;
int i, cnt;
if (sla_in->pir < sla_in->cir) {
dev_notice(&GET_DEV(accel_dev),
"PIR must be >= CIR, setting PIR to CIR\n");
sla_in->pir = sla_in->cir;
}
if (!is_update) {
cnt = 0;
rp_mask_size = sizeof(sla_in->rp_mask) * BITS_PER_BYTE;
for_each_set_bit(i, &rp_mask, rp_mask_size) {
if (++cnt > RL_RP_CNT_PER_LEAF_MAX) {
dev_notice(&GET_DEV(accel_dev),
"Too many ring pairs selected for this SLA\n");
return -EINVAL;
}
}
if (sla_in->srv >= ADF_SVC_NONE) {
dev_notice(&GET_DEV(accel_dev),
"Wrong service type\n");
return -EINVAL;
}
if (sla_in->type > RL_LEAF) {
dev_notice(&GET_DEV(accel_dev),
"Wrong node type\n");
return -EINVAL;
}
if (sla_in->parent_id < RL_PARENT_DEFAULT_ID ||
sla_in->parent_id >= RL_NODES_CNT_MAX) {
dev_notice(&GET_DEV(accel_dev),
"Wrong parent ID\n");
return -EINVAL;
}
}
return 0;
}
static int validate_sla_id(struct adf_accel_dev *accel_dev, int sla_id)
{
struct rl_sla *sla;
if (sla_id <= RL_SLA_EMPTY_ID || sla_id >= RL_NODES_CNT_MAX) {
dev_notice(&GET_DEV(accel_dev), "Provided ID is out of bounds\n");
return -EINVAL;
}
sla = accel_dev->rate_limiting->sla[sla_id];
if (!sla) {
dev_notice(&GET_DEV(accel_dev), "SLA with provided ID does not exist\n");
return -EINVAL;
}
if (sla->type != RL_LEAF) {
dev_notice(&GET_DEV(accel_dev), "This ID is reserved for internal use\n");
return -EINVAL;
}
return 0;
}
/**
* find_parent() - Find the parent for a new SLA
* @rl_data: pointer to ratelimiting data
* @sla_in: pointer to user input data for a new SLA
*
* Function returns a pointer to the parent SLA. If the parent ID is provided
* as input in the user data, then such ID is validated and the parent SLA
* is returned.
* Otherwise, it returns the default parent SLA (root or cluster) for
* the new object.
*
* Return:
* * Pointer to the parent SLA object
* * NULL - when parent cannot be found
*/
static struct rl_sla *find_parent(struct adf_rl *rl_data,
struct adf_rl_sla_input_data *sla_in)
{
int input_parent_id = sla_in->parent_id;
struct rl_sla *root = NULL;
struct rl_sla *parent_sla;
int i;
if (sla_in->type == RL_ROOT)
return NULL;
if (input_parent_id > RL_PARENT_DEFAULT_ID) {
parent_sla = rl_data->sla[input_parent_id];
/*
* SLA can be a parent if it has the same service as the child
* and its type is higher in the hierarchy,
* for example the parent type of a LEAF must be a CLUSTER.
*/
if (parent_sla && parent_sla->srv == sla_in->srv &&
parent_sla->type == sla_in->type - 1)
return parent_sla;
return NULL;
}
/* If input_parent_id is not valid, get root for this service type. */
for (i = 0; i < RL_ROOT_MAX; i++) {
if (rl_data->root[i] && rl_data