// 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_cfg_services.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 0 U
#define RL_TOKEN_GRANULARITY_PCIEOUT_BUCKET 0 U
#define RL_TOKEN_PCIE_SIZE 64
#define RL_TOKEN_ASYM_SIZE 1024
#define RL_CSR_SIZE 4 U
#define RL_CAPABILITY_MASK GENMASK(6 , 4 )
#define RL_CAPABILITY_VALUE 0 x70
#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 >= SVC_BASE_COUNT) {
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->root[i]->srv == sla_in->srv) {
root = rl_data->root[i];
break ;
}
}
if (!root)
return NULL;
/*
* If the type of this SLA is cluster , then return the root .
* Otherwise , find the default ( i . e . first ) cluster for this service .
*/
if (sla_in->type == RL_CLUSTER)
return root;
for (i = 0 ; i < RL_CLUSTER_MAX; i++) {
if (rl_data->cluster[i] && rl_data->cluster[i]->parent == root)
return rl_data->cluster[i];
}
return NULL;
}
/**
* adf_rl_get_sla_arr_of_type ( ) - Returns a pointer to SLA type specific array
* @ rl_data : pointer to ratelimiting data
* @ type : SLA type
* @ sla_arr : pointer to variable where requested pointer will be stored
*
* Return : Max number of elements allowed for the returned array
*/
u32 adf_rl_get_sla_arr_of_type(struct adf_rl *rl_data, enum rl_node_type type,
struct rl_sla ***sla_arr)
{
switch (type) {
case RL_LEAF:
*sla_arr = rl_data->leaf;
return RL_LEAF_MAX;
case RL_CLUSTER:
*sla_arr = rl_data->cluster;
return RL_CLUSTER_MAX;
case RL_ROOT:
*sla_arr = rl_data->root;
return RL_ROOT_MAX;
default :
*sla_arr = NULL;
return 0 ;
}
}
/**
* prepare_rp_ids ( ) - Creates an array of ring pair IDs from bitmask
* @ accel_dev : pointer to acceleration device structure
* @ sla : SLA object data where result will be written
* @ rp_mask : bitmask of ring pair IDs
*
* Function tries to convert provided bitmap to an array of IDs . It checks if
* RPs aren ' t in use , are assigned to SLA service or if a number of provided
* IDs is not too big . If successful , writes the result into the field
* sla - > ring_pairs_cnt .
*
* Return :
* * 0 - ok
* * - EINVAL - ring pairs array cannot be created from provided mask
*/
static int prepare_rp_ids(struct adf_accel_dev *accel_dev, struct rl_sla *sla,
const unsigned long rp_mask)
{
enum adf_cfg_service_type arb_srv = adf_srv_to_cfg_svc_type(sla->srv);
u16 rps_per_bundle = GET_HW_DATA(accel_dev)->num_banks_per_vf;
bool *rp_in_use = accel_dev->rate_limiting->rp_in_use;
size_t rp_cnt_max = ARRAY_SIZE(sla->ring_pairs_ids);
u16 rp_id_max = GET_HW_DATA(accel_dev)->num_banks;
u16 cnt = 0 ;
u16 rp_id;
for_each_set_bit(rp_id, &rp_mask, rp_id_max) {
if (cnt >= rp_cnt_max) {
dev_notice(&GET_DEV(accel_dev),
"Assigned more ring pairs than supported" );
return -EINVAL;
}
if (rp_in_use[rp_id]) {
dev_notice(&GET_DEV(accel_dev),
"RP %u already assigned to other SLA" , rp_id);
return -EINVAL;
}
if (GET_SRV_TYPE(accel_dev, rp_id % rps_per_bundle) != arb_srv) {
dev_notice(&GET_DEV(accel_dev),
"RP %u does not support SLA service" , rp_id);
return -EINVAL;
}
sla->ring_pairs_ids[cnt++] = rp_id;
}
sla->ring_pairs_cnt = cnt;
return 0 ;
}
static void mark_rps_usage(struct rl_sla *sla, bool *rp_in_use, bool used)
{
u16 rp_id;
int i;
for (i = 0 ; i < sla->ring_pairs_cnt; i++) {
rp_id = sla->ring_pairs_ids[i];
rp_in_use[rp_id] = used;
}
}
static void assign_rps_to_leaf(struct adf_accel_dev *accel_dev,
struct rl_sla *sla, bool clear)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
u32 base_offset = hw_data->rl_data.r2l_offset;
u32 node_id = clear ? 0 U : (sla->node_id & LEAF_MASK);
u32 offset;
int i;
for (i = 0 ; i < sla->ring_pairs_cnt; i++) {
offset = base_offset + (RL_CSR_SIZE * sla->ring_pairs_ids[i]);
ADF_CSR_WR(pmisc_addr, offset, node_id);
}
}
static void assign_leaf_to_cluster(struct adf_accel_dev *accel_dev,
struct rl_sla *sla, bool clear)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
u32 base_offset = hw_data->rl_data.l2c_offset;
u32 node_id = sla->node_id & LEAF_MASK;
u32 parent_id = clear ? 0 U : (sla->parent->node_id & CLUSTER_MASK);
u32 offset;
offset = base_offset + (RL_CSR_SIZE * node_id);
ADF_CSR_WR(pmisc_addr, offset, parent_id);
}
static void assign_cluster_to_root(struct adf_accel_dev *accel_dev,
struct rl_sla *sla, bool clear)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
u32 base_offset = hw_data->rl_data.c2s_offset;
u32 node_id = sla->node_id & CLUSTER_MASK;
u32 parent_id = clear ? 0 U : (sla->parent->node_id & ROOT_MASK);
u32 offset;
offset = base_offset + (RL_CSR_SIZE * node_id);
ADF_CSR_WR(pmisc_addr, offset, parent_id);
}
static void assign_node_to_parent(struct adf_accel_dev *accel_dev,
struct rl_sla *sla, bool clear_assignment)
{
switch (sla->type) {
case RL_LEAF:
assign_rps_to_leaf(accel_dev, sla, clear_assignment);
assign_leaf_to_cluster(accel_dev, sla, clear_assignment);
break ;
case RL_CLUSTER:
assign_cluster_to_root(accel_dev, sla, clear_assignment);
break ;
default :
break ;
}
}
/**
* can_parent_afford_sla ( ) - Verifies if parent allows to create an SLA
* @ sla_in : pointer to user input data for a new SLA
* @ sla_parent : pointer to parent SLA object
* @ sla_cir : current child CIR value ( only for update )
* @ is_update : request is a update
*
* Algorithm verifies if parent has enough remaining budget to take assignment
* of a child with provided parameters . In update case current CIR value must be
* returned to budget first .
* PIR value cannot exceed the PIR assigned to parent .
*
* Return :
* * true - SLA can be created
* * false - SLA cannot be created
*/
static bool can_parent_afford_sla(struct adf_rl_sla_input_data *sla_in,
struct rl_sla *sla_parent, u32 sla_cir,
bool is_update)
{
u32 rem_cir = sla_parent->rem_cir;
if (is_update)
rem_cir += sla_cir;
if (sla_in->cir > rem_cir || sla_in->pir > sla_parent->pir)
return false ;
return true ;
}
/**
* can_node_afford_update ( ) - Verifies if SLA can be updated with input data
* @ sla_in : pointer to user input data for a new SLA
* @ sla : pointer to SLA object selected for update
*
* Algorithm verifies if a new CIR value is big enough to satisfy currently
* assigned child SLAs and if PIR can be updated
*
* Return :
* * true - SLA can be updated
* * false - SLA cannot be updated
*/
static bool can_node_afford_update(struct adf_rl_sla_input_data *sla_in,
struct rl_sla *sla)
{
u32 cir_in_use = sla->cir - sla->rem_cir;
/* new CIR cannot be smaller then currently consumed value */
if (cir_in_use > sla_in->cir)
return false ;
/* PIR of root/cluster cannot be reduced in node with assigned children */
if (sla_in->pir < sla->pir && sla->type != RL_LEAF && cir_in_use > 0 )
return false ;
return true ;
}
static bool is_enough_budget(struct adf_rl *rl_data, struct rl_sla *sla,
struct adf_rl_sla_input_data *sla_in,
bool is_update)
{
u32 max_val = rl_data->device_data->scale_ref;
struct rl_sla *parent = sla->parent;
bool ret = true ;
if (sla_in->cir > max_val || sla_in->pir > max_val)
ret = false ;
switch (sla->type) {
case RL_LEAF:
ret &= can_parent_afford_sla(sla_in, parent, sla->cir,
is_update);
break ;
case RL_CLUSTER:
ret &= can_parent_afford_sla(sla_in, parent, sla->cir,
is_update);
if (is_update)
ret &= can_node_afford_update(sla_in, sla);
break ;
case RL_ROOT:
if (is_update)
ret &= can_node_afford_update(sla_in, sla);
break ;
default :
ret = false ;
break ;
}
return ret;
}
static void update_budget(struct rl_sla *sla, u32 old_cir, bool is_update)
{
switch (sla->type) {
case RL_LEAF:
if (is_update)
sla->parent->rem_cir += old_cir;
sla->parent->rem_cir -= sla->cir;
sla->rem_cir = 0 ;
break ;
case RL_CLUSTER:
if (is_update) {
sla->parent->rem_cir += old_cir;
sla->rem_cir = sla->cir - (old_cir - sla->rem_cir);
} else {
sla->rem_cir = sla->cir;
}
sla->parent->rem_cir -= sla->cir;
break ;
case RL_ROOT:
if (is_update)
sla->rem_cir = sla->cir - (old_cir - sla->rem_cir);
else
sla->rem_cir = sla->cir;
break ;
default :
break ;
}
}
/**
* get_next_free_sla_id ( ) - finds next free ID in the SLA array
* @ rl_data : Pointer to ratelimiting data structure
*
* Return :
* * 0 : RL_NODES_CNT_MAX - correct ID
* * - ENOSPC - all SLA slots are in use
*/
static int get_next_free_sla_id(struct adf_rl *rl_data)
{
int i = 0 ;
while (i < RL_NODES_CNT_MAX && rl_data->sla[i++])
;
if (i == RL_NODES_CNT_MAX)
return -ENOSPC;
return i - 1 ;
}
/**
* get_next_free_node_id ( ) - finds next free ID in the array of that node type
* @ rl_data : Pointer to ratelimiting data structure
* @ sla : Pointer to SLA object for which the ID is searched
*
* Return :
* * 0 : RL_ [ NODE_TYPE ] _ MAX - correct ID
* * - ENOSPC - all slots of that type are in use
*/
static int get_next_free_node_id(struct adf_rl *rl_data, struct rl_sla *sla)
{
struct adf_hw_device_data *hw_device = GET_HW_DATA(rl_data->accel_dev);
int max_id, i, step, rp_per_leaf;
struct rl_sla **sla_list;
rp_per_leaf = hw_device->num_banks / hw_device->num_banks_per_vf;
/*
* Static nodes mapping :
* root0 - cluster [ 0 , 4 , 8 , 12 ] - leaf [ 0 - 15 ]
* root1 - cluster [ 1 , 5 , 9 , 13 ] - leaf [ 16 - 31 ]
* root2 - cluster [ 2 , 6 , 10 , 14 ] - leaf [ 32 - 47 ]
*/
switch (sla->type) {
case RL_LEAF:
i = sla->srv * rp_per_leaf;
step = 1 ;
max_id = i + rp_per_leaf;
sla_list = rl_data->leaf;
break ;
case RL_CLUSTER:
i = sla->srv;
step = 4 ;
max_id = RL_CLUSTER_MAX;
sla_list = rl_data->cluster;
break ;
case RL_ROOT:
return sla->srv;
default :
return -EINVAL;
}
while (i < max_id && sla_list[i])
i += step;
if (i >= max_id)
return -ENOSPC;
return i;
}
u32 adf_rl_calculate_slice_tokens(struct adf_accel_dev *accel_dev, u32 sla_val,
enum adf_base_services svc_type)
{
struct adf_rl_hw_data *device_data = &accel_dev->hw_device->rl_data;
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
u64 avail_slice_cycles, allocated_tokens;
if (!sla_val)
return 0 ;
/* Handle generation specific slice count adjustment */
avail_slice_cycles = hw_data->clock_frequency;
avail_slice_cycles *= hw_data->get_svc_slice_cnt(accel_dev, svc_type);
do_div(avail_slice_cycles, device_data->scan_interval);
allocated_tokens = avail_slice_cycles * sla_val;
do_div(allocated_tokens, device_data->scale_ref);
return allocated_tokens;
}
static u32 adf_rl_get_num_svc_aes(struct adf_accel_dev *accel_dev,
enum adf_base_services svc)
{
struct adf_rl_hw_data *device_data = &accel_dev->hw_device->rl_data;
if (svc >= SVC_BASE_COUNT)
return 0 ;
return device_data->svc_ae_mask[svc];
}
u32 adf_rl_calculate_ae_cycles(struct adf_accel_dev *accel_dev, u32 sla_val,
enum adf_base_services svc_type)
{
struct adf_rl_hw_data *device_data = &accel_dev->hw_device->rl_data;
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
u64 allocated_ae_cycles, avail_ae_cycles;
if (!sla_val)
return 0 ;
avail_ae_cycles = hw_data->clock_frequency;
avail_ae_cycles *= adf_rl_get_num_svc_aes(accel_dev, svc_type);
do_div(avail_ae_cycles, device_data->scan_interval);
sla_val *= device_data->max_tp[svc_type];
sla_val /= device_data->scale_ref;
allocated_ae_cycles = (sla_val * avail_ae_cycles);
do_div(allocated_ae_cycles, device_data->max_tp[svc_type]);
return allocated_ae_cycles;
}
u32 adf_rl_calculate_pci_bw(struct adf_accel_dev *accel_dev, u32 sla_val,
enum adf_base_services svc_type, bool is_bw_out)
{
struct adf_rl_hw_data *device_data = &accel_dev->hw_device->rl_data;
u64 sla_to_bytes, allocated_bw, sla_scaled;
if (!sla_val)
return 0 ;
sla_to_bytes = sla_val;
sla_to_bytes *= device_data->max_tp[svc_type];
do_div(sla_to_bytes, device_data->scale_ref);
sla_to_bytes *= (svc_type == SVC_ASYM) ? RL_TOKEN_ASYM_SIZE : BYTES_PER_MBIT;
if (svc_type == SVC_DC && is_bw_out)
sla_to_bytes *= device_data->slices.dcpr_cnt -
device_data->dcpr_correction;
sla_scaled = sla_to_bytes * device_data->pcie_scale_mul;
do_div(sla_scaled, device_data->pcie_scale_div);
allocated_bw = sla_scaled;
do_div(allocated_bw, RL_TOKEN_PCIE_SIZE);
do_div(allocated_bw, device_data->scan_interval);
return allocated_bw;
}
/**
* add_new_sla_entry ( ) - creates a new SLA object and fills it with user data
* @ accel_dev : pointer to acceleration device structure
* @ sla_in : pointer to user input data for a new SLA
* @ sla_out : Pointer to variable that will contain the address of a new
* SLA object if the operation succeeds
*
* Return :
* * 0 - ok
* * - ENOMEM - memory allocation failed
* * - EINVAL - invalid user input
* * - ENOSPC - all available SLAs are in use
*/
static int add_new_sla_entry(struct adf_accel_dev *accel_dev,
struct adf_rl_sla_input_data *sla_in,
struct rl_sla **sla_out)
{
struct adf_rl *rl_data = accel_dev->rate_limiting;
struct rl_sla *sla;
int ret = 0 ;
sla = kzalloc(sizeof (*sla), GFP_KERNEL);
if (!sla) {
ret = -ENOMEM;
goto ret_err;
}
*sla_out = sla;
if (!adf_is_service_enabled(accel_dev, sla_in->srv)) {
dev_notice(&GET_DEV(accel_dev),
"Provided service is not enabled\n" );
ret = -EINVAL;
goto ret_err;
}
sla->srv = sla_in->srv;
sla->type = sla_in->type;
ret = get_next_free_node_id(rl_data, sla);
if (ret < 0 ) {
dev_notice(&GET_DEV(accel_dev),
"Exceeded number of available nodes for that service\n" );
goto ret_err;
}
sla->node_id = ret;
ret = get_next_free_sla_id(rl_data);
if (ret < 0 ) {
dev_notice(&GET_DEV(accel_dev),
"Allocated maximum SLAs number\n" );
goto ret_err;
}
sla->sla_id = ret;
sla->parent = find_parent(rl_data, sla_in);
if (!sla->parent && sla->type != RL_ROOT) {
if (sla_in->parent_id != RL_PARENT_DEFAULT_ID)
dev_notice(&GET_DEV(accel_dev),
"Provided parent ID does not exist or cannot be parent for this SLA." );
else
dev_notice(&GET_DEV(accel_dev),
"Unable to find parent node for this service. Is service enabled?" );
ret = -EINVAL;
goto ret_err;
}
if (sla->type == RL_LEAF) {
ret = prepare_rp_ids(accel_dev, sla, sla_in->rp_mask);
if (!sla->ring_pairs_cnt || ret) {
dev_notice(&GET_DEV(accel_dev),
"Unable to find ring pairs to assign to the leaf" );
if (!ret)
ret = -EINVAL;
goto ret_err;
}
}
return 0 ;
ret_err:
kfree(sla);
*sla_out = NULL;
return ret;
}
static int initialize_default_nodes(struct adf_accel_dev *accel_dev)
{
struct adf_rl *rl_data = accel_dev->rate_limiting;
struct adf_rl_hw_data *device_data = rl_data->device_data;
struct adf_rl_sla_input_data sla_in = { };
int ret = 0 ;
int i;
/* Init root for each enabled service */
sla_in.type = RL_ROOT;
sla_in.parent_id = RL_PARENT_DEFAULT_ID;
for (i = 0 ; i < SVC_BASE_COUNT; i++) {
if (!adf_is_service_enabled(accel_dev, i))
continue ;
sla_in.cir = device_data->scale_ref;
sla_in.pir = sla_in.cir;
sla_in.srv = i;
ret = adf_rl_add_sla(accel_dev, &sla_in);
if (ret)
return ret;
}
/* Init default cluster for each root */
sla_in.type = RL_CLUSTER;
for (i = 0 ; i < SVC_BASE_COUNT; i++) {
if (!rl_data->root[i])
continue ;
sla_in.cir = rl_data->root[i]->cir;
sla_in.pir = sla_in.cir;
sla_in.srv = rl_data->root[i]->srv;
ret = adf_rl_add_sla(accel_dev, &sla_in);
if (ret)
return ret;
}
return 0 ;
}
static void clear_sla(struct adf_rl *rl_data, struct rl_sla *sla)
{
bool *rp_in_use = rl_data->rp_in_use;
struct rl_sla **sla_type_arr = NULL;
int i, sla_id, node_id;
u32 old_cir;
sla_id = sla->sla_id;
node_id = sla->node_id;
old_cir = sla->cir;
sla->cir = 0 ;
sla->pir = 0 ;
for (i = 0 ; i < sla->ring_pairs_cnt; i++)
rp_in_use[sla->ring_pairs_ids[i]] = false ;
update_budget(sla, old_cir, true );
adf_rl_get_sla_arr_of_type(rl_data, sla->type, &sla_type_arr);
assign_node_to_parent(rl_data->accel_dev, sla, true );
adf_rl_send_admin_delete_msg(rl_data->accel_dev, node_id, sla->type);
mark_rps_usage(sla, rl_data->rp_in_use, false );
kfree(sla);
rl_data->sla[sla_id] = NULL;
sla_type_arr[node_id] = NULL;
}
static void free_all_sla(struct adf_accel_dev *accel_dev)
{
struct adf_rl *rl_data = accel_dev->rate_limiting;
int sla_id;
mutex_lock(&rl_data->rl_lock);
for (sla_id = 0 ; sla_id < RL_NODES_CNT_MAX; sla_id++) {
if (!rl_data->sla[sla_id])
continue ;
kfree(rl_data->sla[sla_id]);
rl_data->sla[sla_id] = NULL;
}
mutex_unlock(&rl_data->rl_lock);
}
/**
* add_update_sla ( ) - handles the creation and the update of an SLA
* @ accel_dev : pointer to acceleration device structure
* @ sla_in : pointer to user input data for a new / updated SLA
* @ is_update : flag to indicate if this is an update or an add operation
*
* Return :
* * 0 - ok
* * - ENOMEM - memory allocation failed
* * - EINVAL - user input data cannot be used to create SLA
* * - ENOSPC - all available SLAs are in use
*/
static int add_update_sla(struct adf_accel_dev *accel_dev,
struct adf_rl_sla_input_data *sla_in, bool is_update)
{
struct adf_rl *rl_data = accel_dev->rate_limiting;
struct rl_sla **sla_type_arr = NULL;
struct rl_sla *sla = NULL;
u32 old_cir = 0 ;
int ret;
if (!sla_in) {
dev_warn(&GET_DEV(accel_dev),
"SLA input data pointer is missing\n" );
return -EFAULT;
}
mutex_lock(&rl_data->rl_lock);
/* Input validation */
ret = validate_user_input(accel_dev, sla_in, is_update);
if (ret)
goto ret_err;
if (is_update) {
ret = validate_sla_id(accel_dev, sla_in->sla_id);
if (ret)
goto ret_err;
sla = rl_data->sla[sla_in->sla_id];
old_cir = sla->cir;
} else {
ret = add_new_sla_entry(accel_dev, sla_in, &sla);
if (ret)
goto ret_err;
}
if (!is_enough_budget(rl_data, sla, sla_in, is_update)) {
dev_notice(&GET_DEV(accel_dev),
"Input value exceeds the remaining budget%s\n" ,
is_update ? " or more budget is already in use" : "" );
ret = -EINVAL;
goto ret_err;
}
sla->cir = sla_in->cir;
sla->pir = sla_in->pir;
/* Apply SLA */
assign_node_to_parent(accel_dev, sla, false );
ret = adf_rl_send_admin_add_update_msg(accel_dev, sla, is_update);
if (ret) {
dev_notice(&GET_DEV(accel_dev),
"Failed to apply an SLA\n" );
goto ret_err;
}
update_budget(sla, old_cir, is_update);
if (!is_update) {
mark_rps_usage(sla, rl_data->rp_in_use, true );
adf_rl_get_sla_arr_of_type(rl_data, sla->type, &sla_type_arr);
sla_type_arr[sla->node_id] = sla;
rl_data->sla[sla->sla_id] = sla;
}
sla_in->sla_id = sla->sla_id;
goto ret_ok;
ret_err:
if (!is_update) {
sla_in->sla_id = -1 ;
kfree(sla);
}
ret_ok:
mutex_unlock(&rl_data->rl_lock);
return ret;
}
/**
* adf_rl_add_sla ( ) - handles the creation of an SLA
* @ accel_dev : pointer to acceleration device structure
* @ sla_in : pointer to user input data required to add an SLA
*
* Return :
* * 0 - ok
* * - ENOMEM - memory allocation failed
* * - EINVAL - invalid user input
* * - ENOSPC - all available SLAs are in use
*/
int adf_rl_add_sla(struct adf_accel_dev *accel_dev,
struct adf_rl_sla_input_data *sla_in)
{
return add_update_sla(accel_dev, sla_in, false );
}
/**
* adf_rl_update_sla ( ) - handles the update of an SLA
* @ accel_dev : pointer to acceleration device structure
* @ sla_in : pointer to user input data required to update an SLA
*
* Return :
* * 0 - ok
* * - EINVAL - user input data cannot be used to update SLA
*/
int adf_rl_update_sla(struct adf_accel_dev *accel_dev,
struct adf_rl_sla_input_data *sla_in)
{
return add_update_sla(accel_dev, sla_in, true );
}
/**
* adf_rl_get_sla ( ) - returns an existing SLA data
* @ accel_dev : pointer to acceleration device structure
* @ sla_in : pointer to user data where SLA info will be stored
*
* The sla_id for which data are requested should be set in sla_id structure
*
* Return :
* * 0 - ok
* * - EINVAL - provided sla_id does not exist
*/
int adf_rl_get_sla(struct adf_accel_dev *accel_dev,
struct adf_rl_sla_input_data *sla_in)
{
struct rl_sla *sla;
int ret, i;
ret = validate_sla_id(accel_dev, sla_in->sla_id);
if (ret)
return ret;
sla = accel_dev->rate_limiting->sla[sla_in->sla_id];
sla_in->type = sla->type;
sla_in->srv = sla->srv;
sla_in->cir = sla->cir;
sla_in->pir = sla->pir;
sla_in->rp_mask = 0 U;
if (sla->parent)
sla_in->parent_id = sla->parent->sla_id;
else
sla_in->parent_id = RL_PARENT_DEFAULT_ID;
for (i = 0 ; i < sla->ring_pairs_cnt; i++)
sla_in->rp_mask |= BIT(sla->ring_pairs_ids[i]);
return 0 ;
}
/**
* adf_rl_get_capability_remaining ( ) - returns the remaining SLA value ( CIR ) for
* selected service or provided sla_id
* @ accel_dev : pointer to acceleration device structure
* @ srv : service ID for which capability is requested
* @ sla_id : ID of the cluster or root to which we want assign a new SLA
*
* Check if the provided SLA id is valid . If it is and the service matches
* the requested service and the type is cluster or root , return the remaining
* capability .
* If the provided ID does not match the service or type , return the remaining
* capacity of the default cluster for that service .
*
* Return :
* * Positive value - correct remaining value
* * - EINVAL - algorithm cannot find a remaining value for provided data
*/
int adf_rl_get_capability_remaining(struct adf_accel_dev *accel_dev,
enum adf_base_services srv, int sla_id)
{
struct adf_rl *rl_data = accel_dev->rate_limiting;
struct rl_sla *sla = NULL;
int i;
if (srv >= SVC_BASE_COUNT)
return -EINVAL;
if (sla_id > RL_SLA_EMPTY_ID && !validate_sla_id(accel_dev, sla_id)) {
sla = rl_data->sla[sla_id];
if (sla->srv == srv && sla->type <= RL_CLUSTER)
goto ret_ok;
}
for (i = 0 ; i < RL_CLUSTER_MAX; i++) {
if (!rl_data->cluster[i])
continue ;
if (rl_data->cluster[i]->srv == srv) {
sla = rl_data->cluster[i];
goto ret_ok;
}
}
return -EINVAL;
ret_ok:
return sla->rem_cir;
}
/**
* adf_rl_remove_sla ( ) - removes provided sla_id
* @ accel_dev : pointer to acceleration device structure
* @ sla_id : ID of the cluster or root to which we want assign an new SLA
*
* Return :
* * 0 - ok
* * - EINVAL - wrong sla_id or it still have assigned children
*/
int adf_rl_remove_sla(struct adf_accel_dev *accel_dev, u32 sla_id)
{
struct adf_rl *rl_data = accel_dev->rate_limiting;
struct rl_sla *sla;
int ret = 0 ;
mutex_lock(&rl_data->rl_lock);
ret = validate_sla_id(accel_dev, sla_id);
if (ret)
goto err_ret;
sla = rl_data->sla[sla_id];
if (sla->type < RL_LEAF && sla->rem_cir != sla->cir) {
dev_notice(&GET_DEV(accel_dev),
"To remove parent SLA all its children must be removed first" );
ret = -EINVAL;
goto err_ret;
}
clear_sla(rl_data, sla);
err_ret:
mutex_unlock(&rl_data->rl_lock);
return ret;
}
/**
* adf_rl_remove_sla_all ( ) - removes all SLAs from device
* @ accel_dev : pointer to acceleration device structure
* @ incl_default : set to true if default SLAs also should be removed
*/
void adf_rl_remove_sla_all(struct adf_accel_dev *accel_dev, bool incl_default)
{
struct adf_rl *rl_data = accel_dev->rate_limiting;
int end_type = incl_default ? RL_ROOT : RL_LEAF;
struct rl_sla **sla_type_arr = NULL;
u32 max_id;
int i, j;
mutex_lock(&rl_data->rl_lock);
/* Unregister and remove all SLAs */
for (j = RL_LEAF; j >= end_type; j--) {
max_id = adf_rl_get_sla_arr_of_type(rl_data, j, &sla_type_arr);
for (i = 0 ; i < max_id; i++) {
if (!sla_type_arr[i])
continue ;
clear_sla(rl_data, sla_type_arr[i]);
}
}
mutex_unlock(&rl_data->rl_lock);
}
int adf_rl_init(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
struct adf_rl_hw_data *rl_hw_data = &hw_data->rl_data;
struct adf_rl *rl;
int ret = 0 ;
/* Validate device parameters */
if (RL_VALIDATE_NON_ZERO(rl_hw_data->max_tp[SVC_ASYM]) ||
RL_VALIDATE_NON_ZERO(rl_hw_data->max_tp[SVC_SYM]) ||
RL_VALIDATE_NON_ZERO(rl_hw_data->max_tp[SVC_DC]) ||
RL_VALIDATE_NON_ZERO(rl_hw_data->scan_interval) ||
RL_VALIDATE_NON_ZERO(rl_hw_data->pcie_scale_div) ||
RL_VALIDATE_NON_ZERO(rl_hw_data->pcie_scale_mul) ||
RL_VALIDATE_NON_ZERO(rl_hw_data->scale_ref)) {
ret = -EOPNOTSUPP;
goto err_ret;
}
rl = kzalloc(sizeof (*rl), GFP_KERNEL);
if (!rl) {
ret = -ENOMEM;
goto err_ret;
}
mutex_init(&rl->rl_lock);
rl->device_data = &accel_dev->hw_device->rl_data;
rl->accel_dev = accel_dev;
init_rwsem(&rl->user_input.lock);
accel_dev->rate_limiting = rl;
err_ret:
return ret;
}
int adf_rl_start(struct adf_accel_dev *accel_dev)
{
struct adf_rl_hw_data *rl_hw_data = &GET_HW_DATA(accel_dev)->rl_data;
void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
u16 fw_caps = GET_HW_DATA(accel_dev)->fw_capabilities;
int ret;
if (!accel_dev->rate_limiting) {
ret = -EOPNOTSUPP;
goto ret_err;
}
if ((fw_caps & RL_CAPABILITY_MASK) != RL_CAPABILITY_VALUE) {
dev_info(&GET_DEV(accel_dev), "feature not supported by FW\n" );
ret = -EOPNOTSUPP;
goto ret_free;
}
ADF_CSR_WR(pmisc_addr, rl_hw_data->pciin_tb_offset,
RL_TOKEN_GRANULARITY_PCIEIN_BUCKET);
ADF_CSR_WR(pmisc_addr, rl_hw_data->pciout_tb_offset,
RL_TOKEN_GRANULARITY_PCIEOUT_BUCKET);
ret = adf_rl_send_admin_init_msg(accel_dev, &rl_hw_data->slices);
if (ret) {
dev_err(&GET_DEV(accel_dev), "initialization failed\n" );
goto ret_free;
}
ret = initialize_default_nodes(accel_dev);
if (ret) {
dev_err(&GET_DEV(accel_dev),
"failed to initialize default SLAs\n" );
goto ret_sla_rm;
}
ret = adf_sysfs_rl_add(accel_dev);
if (ret) {
dev_err(&GET_DEV(accel_dev), "failed to add sysfs interface\n" );
goto ret_sysfs_rm;
}
return 0 ;
ret_sysfs_rm:
adf_sysfs_rl_rm(accel_dev);
ret_sla_rm:
adf_rl_remove_sla_all(accel_dev, true );
ret_free:
kfree(accel_dev->rate_limiting);
accel_dev->rate_limiting = NULL;
ret_err:
return ret;
}
void adf_rl_stop(struct adf_accel_dev *accel_dev)
{
if (!accel_dev->rate_limiting)
return ;
adf_sysfs_rl_rm(accel_dev);
free_all_sla(accel_dev);
}
void adf_rl_exit(struct adf_accel_dev *accel_dev)
{
if (!accel_dev->rate_limiting)
return ;
kfree(accel_dev->rate_limiting);
accel_dev->rate_limiting = NULL;
}
Messung V0.5 in Prozent C=98 H=83 G=90
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland