/* * Copyright (c) 2020, Pelion and affiliates. * SPDX-License-Identifier: Apache-2.0 * * 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. */ #include "nsconfig.h" #include "ns_types.h" #include "string.h" #include "ns_trace.h" #include "nsdynmemLIB.h" #include "randLIB.h" #include "Service_Libs/random_early_detection/random_early_detection.h" #include "Service_Libs/random_early_detection/random_early_detection_api.h" red_info_t *random_early_detection_create(uint16_t threshold_min, uint16_t threshold_max, uint8_t drop_max_p, uint16_t weight) { //Weight must be between 1-256 if (weight == 0 || weight > 256) { return NULL; } //Probability must be between 1-100 if (drop_max_p == 0 || drop_max_p > 100) { return NULL; } //Max Threshold can't smaller or similar than min if (threshold_max <= threshold_min) { return NULL; } red_info_t *red_info = ns_dyn_mem_alloc(sizeof(red_info_t)); if (red_info) { red_info->count = 0; red_info->averageQ = 0; red_info->parameters.drop_maX_P = drop_max_p; red_info->parameters.threshold_max = threshold_max; red_info->parameters.threshold_min = threshold_min; red_info->parameters.weight = weight; } return red_info; } void random_early_detection_free(struct red_info_s *red_info) { ns_dyn_mem_free(red_info); } //calculate average and return averaged value back uint16_t random_early_detetction_aq_calc(red_info_t *red_info, uint16_t sampleLen) { if (!red_info) { return 0; } if (red_info->parameters.weight == RED_AVERAGE_WEIGHT_DISABLED || red_info->averageQ == 0) { red_info->averageQ = sampleLen * 256; return sampleLen; } // AQ = (1-weight) * average_queue + weight*sampleLen // Now Sample is scaled by 256 which is not loosing so much tail at average //Weight Last Average part (1-weight) * average_queue with scaled 256 uint32_t averageSum = ((256 - red_info->parameters.weight) * red_info->averageQ) / 256; //Add new weighted sample lenght (weight*sampleLen) averageSum += (red_info->parameters.weight * sampleLen); if (averageSum & 1) { //If sum is ODD add 1 this will help to not stuck like 1,99 average to -> 2 averageSum++; } //Store new average red_info->averageQ = averageSum; //Return always same format scaled than inn return red_info->averageQ / 256; } uint16_t random_early_detetction_aq_read(red_info_t *red_info) { if (!red_info) { return 0; } return red_info->averageQ / 256; } bool random_early_detection_congestion_check(red_info_t *red_info) { if (!red_info) { return false; } //Calulate Average queue size uint16_t sampleLen = red_info->averageQ / 256;; if (sampleLen <= red_info->parameters.threshold_min) { //Can be added to queue without any RED operation red_info->count = 0; return false; } if (sampleLen > red_info->parameters.threshold_max) { //Always drop over threshold_max red_info->count = 0; return true; } // Calculate probability for packet drop // tempP = drop_maX_P *(AQ - threshold_min) / (threshold_max - threshold_min); uint32_t tempP = (uint32_t) red_info->parameters.drop_maX_P * PROB_SCALE * (sampleLen - red_info->parameters.threshold_min) / (red_info->parameters.threshold_max - red_info->parameters.threshold_min); // Next Prob = tempP / (1 - count*tempP) // This will increase probability and //Calculate first divider part uint32_t Prob = red_info->count * tempP; //Check that divider it is not >= 0 if (Prob >= PROB_SCALE_MAX) { red_info->count = 0; return true; } //Calculate only when count * tempP is smaller than scaler Prob = (tempP * PROB_SCALE_MAX) / (PROB_SCALE_MAX - Prob); if (Prob > randLIB_get_random_in_range(0, PROX_MAX_RANDOM)) { //Drop packet red_info->count = 0; return true; } //Increment count next round check red_info->count++; return false; }