summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
blob: b3a249b2a482fbd65ee6e857c572d66727d4ad45 (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
152
153
154
155
156
157
158
159
160
161
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
// Copyright (c) 2018 Mellanox Technologies

#include "en.h"
#include "en/hv_vhca_stats.h"
#include "lib/hv_vhca.h"
#include "lib/hv.h"

struct mlx5e_hv_vhca_per_ring_stats {
	u64     rx_packets;
	u64     rx_bytes;
	u64     tx_packets;
	u64     tx_bytes;
};

static void
mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch,
			      struct mlx5e_hv_vhca_per_ring_stats *data)
{
	struct mlx5e_channel_stats *stats;
	int tc;

	stats = &priv->channel_stats[ch];
	data->rx_packets = stats->rq.packets;
	data->rx_bytes   = stats->rq.bytes;

	for (tc = 0; tc < priv->max_opened_tc; tc++) {
		data->tx_packets += stats->sq[tc].packets;
		data->tx_bytes   += stats->sq[tc].bytes;
	}
}

static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data,
				     int buf_len)
{
	int ch, i = 0;

	for (ch = 0; ch < priv->max_nch; ch++) {
		void *buf = data + i;

		if (WARN_ON_ONCE(buf +
				 sizeof(struct mlx5e_hv_vhca_per_ring_stats) >
				 data + buf_len))
			return;

		mlx5e_hv_vhca_fill_ring_stats(priv, ch, buf);
		i += sizeof(struct mlx5e_hv_vhca_per_ring_stats);
	}
}

static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv)
{
	return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) *
		priv->max_nch);
}

static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
{
	struct mlx5e_hv_vhca_stats_agent *sagent;
	struct mlx5_hv_vhca_agent *agent;
	struct delayed_work *dwork;
	struct mlx5e_priv *priv;
	int buf_len, rc;
	void *buf;

	dwork = to_delayed_work(work);
	sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work);
	priv = container_of(sagent, struct mlx5e_priv, stats_agent);
	buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
	agent = sagent->agent;
	buf = sagent->buf;

	memset(buf, 0, buf_len);
	mlx5e_hv_vhca_fill_stats(priv, buf, buf_len);

	rc = mlx5_hv_vhca_agent_write(agent, buf, buf_len);
	if (rc) {
		mlx5_core_err(priv->mdev,
			      "%s: Failed to write stats, err = %d\n",
			      __func__, rc);
		return;
	}

	if (sagent->delay)
		queue_delayed_work(priv->wq, &sagent->work, sagent->delay);
}

enum {
	MLX5_HV_VHCA_STATS_VERSION     = 1,
	MLX5_HV_VHCA_STATS_UPDATE_ONCE = 0xFFFF,
};

static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent,
					struct mlx5_hv_vhca_control_block *block)
{
	struct mlx5e_hv_vhca_stats_agent *sagent;
	struct mlx5e_priv *priv;

	priv = mlx5_hv_vhca_agent_priv(agent);
	sagent = &priv->stats_agent;

	block->version = MLX5_HV_VHCA_STATS_VERSION;
	block->rings   = priv->max_nch;

	if (!block->command) {
		cancel_delayed_work_sync(&priv->stats_agent.work);
		return;
	}

	sagent->delay = block->command == MLX5_HV_VHCA_STATS_UPDATE_ONCE ? 0 :
			msecs_to_jiffies(block->command * 100);

	queue_delayed_work(priv->wq, &sagent->work, sagent->delay);
}

static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent)
{
	struct mlx5e_priv *priv = mlx5_hv_vhca_agent_priv(agent);

	cancel_delayed_work_sync(&priv->stats_agent.work);
}

int mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
{
	int buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
	struct mlx5_hv_vhca_agent *agent;

	priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL);
	if (!priv->stats_agent.buf)
		return -ENOMEM;

	agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca,
					  MLX5_HV_VHCA_AGENT_STATS,
					  mlx5e_hv_vhca_stats_control, NULL,
					  mlx5e_hv_vhca_stats_cleanup,
					  priv);

	if (IS_ERR_OR_NULL(agent)) {
		if (IS_ERR(agent))
			netdev_warn(priv->netdev,
				    "Failed to create hv vhca stats agent, err = %ld\n",
				    PTR_ERR(agent));

		kfree(priv->stats_agent.buf);
		return IS_ERR_OR_NULL(agent);
	}

	priv->stats_agent.agent = agent;
	INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);

	return 0;
}

void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
{
	if (IS_ERR_OR_NULL(priv->stats_agent.agent))
		return;

	mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent);
	kfree(priv->stats_agent.buf);
}