1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2 /* 3 * Copyright (c) 2018, Mellanox Technologies inc. All rights reserved. 4 */ 5 6 #include <linux/dim.h> 7 8 struct dim_cq_moder 9 net_dim_get_rx_moderation(u8 cq_period_mode, int ix) 10 { 11 struct dim_cq_moder cq_moder = rx_profile[cq_period_mode][ix]; 12 13 cq_moder.cq_period_mode = cq_period_mode; 14 return cq_moder; 15 } 16 EXPORT_SYMBOL(net_dim_get_rx_moderation); 17 18 struct dim_cq_moder 19 net_dim_get_def_rx_moderation(u8 cq_period_mode) 20 { 21 u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ? 22 NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE; 23 24 return net_dim_get_rx_moderation(cq_period_mode, profile_ix); 25 } 26 EXPORT_SYMBOL(net_dim_get_def_rx_moderation); 27 28 struct dim_cq_moder 29 net_dim_get_tx_moderation(u8 cq_period_mode, int ix) 30 { 31 struct dim_cq_moder cq_moder = tx_profile[cq_period_mode][ix]; 32 33 cq_moder.cq_period_mode = cq_period_mode; 34 return cq_moder; 35 } 36 EXPORT_SYMBOL(net_dim_get_tx_moderation); 37 38 struct dim_cq_moder 39 net_dim_get_def_tx_moderation(u8 cq_period_mode) 40 { 41 u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ? 42 NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE; 43 44 return net_dim_get_tx_moderation(cq_period_mode, profile_ix); 45 } 46 EXPORT_SYMBOL(net_dim_get_def_tx_moderation); 47 48 static int net_dim_step(struct dim *dim) 49 { 50 if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2)) 51 return DIM_TOO_TIRED; 52 53 switch (dim->tune_state) { 54 case DIM_PARKING_ON_TOP: 55 case DIM_PARKING_TIRED: 56 break; 57 case DIM_GOING_RIGHT: 58 if (dim->profile_ix == (NET_DIM_PARAMS_NUM_PROFILES - 1)) 59 return DIM_ON_EDGE; 60 dim->profile_ix++; 61 dim->steps_right++; 62 break; 63 case DIM_GOING_LEFT: 64 if (dim->profile_ix == 0) 65 return DIM_ON_EDGE; 66 dim->profile_ix--; 67 dim->steps_left++; 68 break; 69 } 70 71 dim->tired++; 72 return DIM_STEPPED; 73 } 74 75 static void net_dim_exit_parking(struct dim *dim) 76 { 77 dim->tune_state = dim->profile_ix ? DIM_GOING_LEFT : DIM_GOING_RIGHT; 78 net_dim_step(dim); 79 } 80 81 static int net_dim_stats_compare(struct dim_stats *curr, 82 struct dim_stats *prev) 83 { 84 if (!prev->bpms) 85 return curr->bpms ? DIM_STATS_BETTER : DIM_STATS_SAME; 86 87 if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms)) 88 return (curr->bpms > prev->bpms) ? DIM_STATS_BETTER : 89 DIM_STATS_WORSE; 90 91 if (!prev->ppms) 92 return curr->ppms ? DIM_STATS_BETTER : 93 DIM_STATS_SAME; 94 95 if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms)) 96 return (curr->ppms > prev->ppms) ? DIM_STATS_BETTER : 97 DIM_STATS_WORSE; 98 99 if (!prev->epms) 100 return DIM_STATS_SAME; 101 102 if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms)) 103 return (curr->epms < prev->epms) ? DIM_STATS_BETTER : 104 DIM_STATS_WORSE; 105 106 return DIM_STATS_SAME; 107 } 108 109 static bool net_dim_decision(struct dim_stats *curr_stats, struct dim *dim) 110 { 111 int prev_state = dim->tune_state; 112 int prev_ix = dim->profile_ix; 113 int stats_res; 114 int step_res; 115 116 switch (dim->tune_state) { 117 case DIM_PARKING_ON_TOP: 118 stats_res = net_dim_stats_compare(curr_stats, 119 &dim->prev_stats); 120 if (stats_res != DIM_STATS_SAME) 121 net_dim_exit_parking(dim); 122 break; 123 124 case DIM_PARKING_TIRED: 125 dim->tired--; 126 if (!dim->tired) 127 net_dim_exit_parking(dim); 128 break; 129 130 case DIM_GOING_RIGHT: 131 case DIM_GOING_LEFT: 132 stats_res = net_dim_stats_compare(curr_stats, 133 &dim->prev_stats); 134 if (stats_res != DIM_STATS_BETTER) 135 dim_turn(dim); 136 137 if (dim_on_top(dim)) { 138 dim_park_on_top(dim); 139 break; 140 } 141 142 step_res = net_dim_step(dim); 143 switch (step_res) { 144 case DIM_ON_EDGE: 145 dim_park_on_top(dim); 146 break; 147 case DIM_TOO_TIRED: 148 dim_park_tired(dim); 149 break; 150 } 151 152 break; 153 } 154 155 if (prev_state != DIM_PARKING_ON_TOP || 156 dim->tune_state != DIM_PARKING_ON_TOP) 157 dim->prev_stats = *curr_stats; 158 159 return dim->profile_ix != prev_ix; 160 } 161 162 void net_dim(struct dim *dim, struct dim_sample end_sample) 163 { 164 struct dim_stats curr_stats; 165 u16 nevents; 166 167 switch (dim->state) { 168 case DIM_MEASURE_IN_PROGRESS: 169 nevents = BIT_GAP(BITS_PER_TYPE(u16), 170 end_sample.event_ctr, 171 dim->start_sample.event_ctr); 172 if (nevents < DIM_NEVENTS) 173 break; 174 dim_calc_stats(&dim->start_sample, &end_sample, &curr_stats); 175 if (net_dim_decision(&curr_stats, dim)) { 176 dim->state = DIM_APPLY_NEW_PROFILE; 177 schedule_work(&dim->work); 178 break; 179 } 180 /* fall through */ 181 case DIM_START_MEASURE: 182 dim_update_sample(end_sample.event_ctr, end_sample.pkt_ctr, 183 end_sample.byte_ctr, &dim->start_sample); 184 dim->state = DIM_MEASURE_IN_PROGRESS; 185 break; 186 case DIM_APPLY_NEW_PROFILE: 187 break; 188 } 189 } 190 EXPORT_SYMBOL(net_dim); 191