xref: /linux/lib/dim/rdma_dim.c (revision f8e17c17b81070f38062dce79ca7f4541851dadd)
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