1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2 /* 3 * Copyright (c) 2019, Mellanox Technologies inc. All rights reserved. 4 */ 5 6 #include <linux/dim.h> 7 8 static int rdma_dim_step(struct dim *dim) 9 { 10 if (dim->tune_state == DIM_GOING_RIGHT) { 11 if (dim->profile_ix == (RDMA_DIM_PARAMS_NUM_PROFILES - 1)) 12 return DIM_ON_EDGE; 13 dim->profile_ix++; 14 dim->steps_right++; 15 } 16 if (dim->tune_state == DIM_GOING_LEFT) { 17 if (dim->profile_ix == 0) 18 return DIM_ON_EDGE; 19 dim->profile_ix--; 20 dim->steps_left++; 21 } 22 23 return DIM_STEPPED; 24 } 25 26 static int rdma_dim_stats_compare(struct dim_stats *curr, 27 struct dim_stats *prev) 28 { 29 /* first stat */ 30 if (!prev->cpms) 31 return DIM_STATS_SAME; 32 33 if (IS_SIGNIFICANT_DIFF(curr->cpms, prev->cpms)) 34 return (curr->cpms > prev->cpms) ? DIM_STATS_BETTER : 35 DIM_STATS_WORSE; 36 37 if (IS_SIGNIFICANT_DIFF(curr->cpe_ratio, prev->cpe_ratio)) 38 return (curr->cpe_ratio > prev->cpe_ratio) ? DIM_STATS_BETTER : 39 DIM_STATS_WORSE; 40 41 return DIM_STATS_SAME; 42 } 43 44 static bool rdma_dim_decision(struct dim_stats *curr_stats, struct dim *dim) 45 { 46 int prev_ix = dim->profile_ix; 47 u8 state = dim->tune_state; 48 int stats_res; 49 int step_res; 50 51 if (state != DIM_PARKING_ON_TOP && state != DIM_PARKING_TIRED) { 52 stats_res = rdma_dim_stats_compare(curr_stats, 53 &dim->prev_stats); 54 55 switch (stats_res) { 56 case DIM_STATS_SAME: 57 if (curr_stats->cpe_ratio <= 50 * prev_ix) 58 dim->profile_ix = 0; 59 break; 60 case DIM_STATS_WORSE: 61 dim_turn(dim); 62 /* fall through */ 63 case DIM_STATS_BETTER: 64 step_res = rdma_dim_step(dim); 65 if (step_res == DIM_ON_EDGE) 66 dim_turn(dim); 67 break; 68 } 69 } 70 71 dim->prev_stats = *curr_stats; 72 73 return dim->profile_ix != prev_ix; 74 } 75 76 void rdma_dim(struct dim *dim, u64 completions) 77 { 78 struct dim_sample *curr_sample = &dim->measuring_sample; 79 struct dim_stats curr_stats; 80 u32 nevents; 81 82 dim_update_sample_with_comps(curr_sample->event_ctr + 1, 0, 0, 83 curr_sample->comp_ctr + completions, 84 &dim->measuring_sample); 85 86 switch (dim->state) { 87 case DIM_MEASURE_IN_PROGRESS: 88 nevents = curr_sample->event_ctr - dim->start_sample.event_ctr; 89 if (nevents < DIM_NEVENTS) 90 break; 91 dim_calc_stats(&dim->start_sample, curr_sample, &curr_stats); 92 if (rdma_dim_decision(&curr_stats, dim)) { 93 dim->state = DIM_APPLY_NEW_PROFILE; 94 schedule_work(&dim->work); 95 break; 96 } 97 /* fall through */ 98 case DIM_START_MEASURE: 99 dim->state = DIM_MEASURE_IN_PROGRESS; 100 dim_update_sample_with_comps(curr_sample->event_ctr, 0, 0, 101 curr_sample->comp_ctr, 102 &dim->start_sample); 103 break; 104 case DIM_APPLY_NEW_PROFILE: 105 break; 106 } 107 } 108 EXPORT_SYMBOL(rdma_dim); 109