aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.c
blob: 254c8473904643f2f88dc08441185c1a809eade1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Mellanox Technologies. */

#include "en.h"
#include "monitor_stats.h"
#include "lib/eq.h"

/* Driver will set the following watch counters list:
 * Ppcnt.802_3:
 * a_in_range_length_errors      Type: 0x0, Counter:  0x0, group_id = N/A
 * a_out_of_range_length_field   Type: 0x0, Counter:  0x1, group_id = N/A
 * a_frame_too_long_errors       Type: 0x0, Counter:  0x2, group_id = N/A
 * a_frame_check_sequence_errors Type: 0x0, Counter:  0x3, group_id = N/A
 * a_alignment_errors            Type: 0x0, Counter:  0x4, group_id = N/A
 * if_out_discards               Type: 0x0, Counter:  0x5, group_id = N/A
 * Q_Counters:
 * Q[index].rx_out_of_buffer   Type: 0x1, Counter:  0x4, group_id = counter_ix
 */

#define NUM_REQ_PPCNT_COUNTER_S1 MLX5_CMD_SET_MONITOR_NUM_PPCNT_COUNTER_SET1
#define NUM_REQ_Q_COUNTERS_S1    MLX5_CMD_SET_MONITOR_NUM_Q_COUNTERS_SET1

int mlx5e_monitor_counter_supported(struct mlx5e_priv *priv)
{
	struct mlx5_core_dev *mdev = priv->mdev;

	if (!MLX5_CAP_GEN(mdev, max_num_of_monitor_counters))
		return false;
	if (MLX5_CAP_PCAM_REG(mdev, ppcnt) &&
	    MLX5_CAP_GEN(mdev, num_ppcnt_monitor_counters) <
	    NUM_REQ_PPCNT_COUNTER_S1)
		return false;
	if (MLX5_CAP_GEN(mdev, num_q_monitor_counters) <
	    NUM_REQ_Q_COUNTERS_S1)
		return false;
	return true;
}

void mlx5e_monitor_counter_arm(struct mlx5e_priv *priv)
{
	u32 in[MLX5_ST_SZ_DW(arm_monitor_counter_in)] = {};

	MLX5_SET(arm_monitor_counter_in, in, opcode,
		 MLX5_CMD_OP_ARM_MONITOR_COUNTER);
	mlx5_cmd_exec_in(priv->mdev, arm_monitor_counter, in);
}

static void mlx5e_monitor_counters_work(struct work_struct *work)
{
	struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
					       monitor_counters_work);

	mutex_lock(&priv->state_lock);
	mlx5e_stats_update_ndo_stats(priv);
	mutex_unlock(&priv->state_lock);
	mlx5e_monitor_counter_arm(priv);
}

static int mlx5e_monitor_event_handler(struct notifier_block *nb,
				       unsigned long event, void *eqe)
{
	struct mlx5e_priv *priv = mlx5_nb_cof(nb, struct mlx5e_priv,
					      monitor_counters_nb);
	queue_work(priv->wq, &priv->monitor_counters_work);
	return NOTIFY_OK;
}

static int fill_monitor_counter_ppcnt_set1(int cnt, u32 *in)
{
	enum mlx5_monitor_counter_ppcnt ppcnt_cnt;

	for (ppcnt_cnt = 0;
	     ppcnt_cnt < NUM_REQ_PPCNT_COUNTER_S1;
	     ppcnt_cnt++, cnt++) {
		MLX5_SET(set_monitor_counter_in, in,
			 monitor_counter[cnt].type,
			 MLX5_QUERY_MONITOR_CNT_TYPE_PPCNT);
		MLX5_SET(set_monitor_counter_in, in,
			 monitor_counter[cnt].counter,
			 ppcnt_cnt);
	}
	return ppcnt_cnt;
}

static int fill_monitor_counter_q_counter_set1(int cnt, int q_counter, u32 *in)
{
	MLX5_SET(set_monitor_counter_in, in,
		 monitor_counter[cnt].type,
		 MLX5_QUERY_MONITOR_CNT_TYPE_Q_COUNTER);
	MLX5_SET(set_monitor_counter_in, in,
		 monitor_counter[cnt].counter,
		 MLX5_QUERY_MONITOR_Q_COUNTER_RX_OUT_OF_BUFFER);
	MLX5_SET(set_monitor_counter_in, in,
		 monitor_counter[cnt].counter_group_id,
		 q_counter);
	return 1;
}

/* check if mlx5e_monitor_counter_supported before calling this function*/
static void mlx5e_set_monitor_counter(struct mlx5e_priv *priv)
{
	struct mlx5_core_dev *mdev = priv->mdev;
	int max_num_of_counters = MLX5_CAP_GEN(mdev, max_num_of_monitor_counters);
	int num_q_counters      = MLX5_CAP_GEN(mdev, num_q_monitor_counters);
	int num_ppcnt_counters  = !MLX5_CAP_PCAM_REG(mdev, ppcnt) ? 0 :
				  MLX5_CAP_GEN(mdev, num_ppcnt_monitor_counters);
	u32 in[MLX5_ST_SZ_DW(set_monitor_counter_in)] = {};
	int q_counter = priv->q_counter;
	int cnt	= 0;

	if (num_ppcnt_counters  >=  NUM_REQ_PPCNT_COUNTER_S1 &&
	    max_num_of_counters >= (NUM_REQ_PPCNT_COUNTER_S1 + cnt))
		cnt += fill_monitor_counter_ppcnt_set1(cnt, in);

	if (num_q_counters      >=  NUM_REQ_Q_COUNTERS_S1 &&
	    max_num_of_counters >= (NUM_REQ_Q_COUNTERS_S1 + cnt) &&
	    q_counter)
		cnt += fill_monitor_counter_q_counter_set1(cnt, q_counter, in);

	MLX5_SET(set_monitor_counter_in, in, num_of_counters, cnt);
	MLX5_SET(set_monitor_counter_in, in, opcode,
		 MLX5_CMD_OP_SET_MONITOR_COUNTER);

	mlx5_cmd_exec_in(mdev, set_monitor_counter, in);
}

/* check if mlx5e_monitor_counter_supported before calling this function*/
void mlx5e_monitor_counter_init(struct mlx5e_priv *priv)
{
	INIT_WORK(&priv->monitor_counters_work, mlx5e_monitor_counters_work);
	MLX5_NB_INIT(&priv->monitor_counters_nb, mlx5e_monitor_event_handler,
		     MONITOR_COUNTER);
	mlx5_eq_notifier_register(priv->mdev, &priv->monitor_counters_nb);

	mlx5e_set_monitor_counter(priv);
	mlx5e_monitor_counter_arm(priv);
	queue_work(priv->wq, &priv->update_stats_work);
}

/* check if mlx5e_monitor_counter_supported before calling this function*/
void mlx5e_monitor_counter_cleanup(struct mlx5e_priv *priv)
{
	u32 in[MLX5_ST_SZ_DW(set_monitor_counter_in)] = {};

	MLX5_SET(set_monitor_counter_in, in, opcode,
		 MLX5_CMD_OP_SET_MONITOR_COUNTER);

	mlx5_cmd_exec_in(priv->mdev, set_monitor_counter, in);
	mlx5_eq_notifier_unregister(priv->mdev, &priv->monitor_counters_nb);
	cancel_work_sync(&priv->monitor_counters_work);
}