1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
2 | // Copyright (c) 2018 Mellanox Technologies |
3 | |
4 | #include "en.h" |
5 | #include "en/hv_vhca_stats.h" |
6 | #include "lib/hv_vhca.h" |
7 | #include "lib/hv.h" |
8 | |
9 | struct mlx5e_hv_vhca_per_ring_stats { |
10 | u64 rx_packets; |
11 | u64 rx_bytes; |
12 | u64 tx_packets; |
13 | u64 tx_bytes; |
14 | }; |
15 | |
16 | static void |
17 | mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch, |
18 | struct mlx5e_hv_vhca_per_ring_stats *data) |
19 | { |
20 | struct mlx5e_channel_stats *stats; |
21 | int tc; |
22 | |
23 | stats = priv->channel_stats[ch]; |
24 | data->rx_packets = stats->rq.packets; |
25 | data->rx_bytes = stats->rq.bytes; |
26 | |
27 | for (tc = 0; tc < priv->max_opened_tc; tc++) { |
28 | data->tx_packets += stats->sq[tc].packets; |
29 | data->tx_bytes += stats->sq[tc].bytes; |
30 | } |
31 | } |
32 | |
33 | static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data, |
34 | int buf_len) |
35 | { |
36 | int ch, i = 0; |
37 | |
38 | for (ch = 0; ch < priv->stats_nch; ch++) { |
39 | void *buf = data + i; |
40 | |
41 | if (WARN_ON_ONCE(buf + |
42 | sizeof(struct mlx5e_hv_vhca_per_ring_stats) > |
43 | data + buf_len)) |
44 | return; |
45 | |
46 | mlx5e_hv_vhca_fill_ring_stats(priv, ch, data: buf); |
47 | i += sizeof(struct mlx5e_hv_vhca_per_ring_stats); |
48 | } |
49 | } |
50 | |
51 | static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv) |
52 | { |
53 | return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) * |
54 | priv->stats_nch); |
55 | } |
56 | |
57 | static void mlx5e_hv_vhca_stats_work(struct work_struct *work) |
58 | { |
59 | struct mlx5e_hv_vhca_stats_agent *sagent; |
60 | struct mlx5_hv_vhca_agent *agent; |
61 | struct delayed_work *dwork; |
62 | struct mlx5e_priv *priv; |
63 | int buf_len, rc; |
64 | void *buf; |
65 | |
66 | dwork = to_delayed_work(work); |
67 | sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work); |
68 | priv = container_of(sagent, struct mlx5e_priv, stats_agent); |
69 | buf_len = mlx5e_hv_vhca_stats_buf_size(priv); |
70 | agent = sagent->agent; |
71 | buf = sagent->buf; |
72 | |
73 | memset(buf, 0, buf_len); |
74 | mlx5e_hv_vhca_fill_stats(priv, data: buf, buf_len); |
75 | |
76 | rc = mlx5_hv_vhca_agent_write(agent, buf, len: buf_len); |
77 | if (rc) { |
78 | mlx5_core_err(priv->mdev, |
79 | "%s: Failed to write stats, err = %d\n" , |
80 | __func__, rc); |
81 | return; |
82 | } |
83 | |
84 | if (sagent->delay) |
85 | queue_delayed_work(wq: priv->wq, dwork: &sagent->work, delay: sagent->delay); |
86 | } |
87 | |
88 | enum { |
89 | MLX5_HV_VHCA_STATS_VERSION = 1, |
90 | MLX5_HV_VHCA_STATS_UPDATE_ONCE = 0xFFFF, |
91 | }; |
92 | |
93 | static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent, |
94 | struct mlx5_hv_vhca_control_block *block) |
95 | { |
96 | struct mlx5e_hv_vhca_stats_agent *sagent; |
97 | struct mlx5e_priv *priv; |
98 | |
99 | priv = mlx5_hv_vhca_agent_priv(agent); |
100 | sagent = &priv->stats_agent; |
101 | |
102 | block->version = MLX5_HV_VHCA_STATS_VERSION; |
103 | block->rings = priv->stats_nch; |
104 | |
105 | if (!block->command) { |
106 | cancel_delayed_work_sync(dwork: &priv->stats_agent.work); |
107 | return; |
108 | } |
109 | |
110 | sagent->delay = block->command == MLX5_HV_VHCA_STATS_UPDATE_ONCE ? 0 : |
111 | msecs_to_jiffies(m: block->command * 100); |
112 | |
113 | queue_delayed_work(wq: priv->wq, dwork: &sagent->work, delay: sagent->delay); |
114 | } |
115 | |
116 | static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent) |
117 | { |
118 | struct mlx5e_priv *priv = mlx5_hv_vhca_agent_priv(agent); |
119 | |
120 | cancel_delayed_work_sync(dwork: &priv->stats_agent.work); |
121 | } |
122 | |
123 | void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) |
124 | { |
125 | int buf_len = mlx5e_hv_vhca_stats_buf_size(priv); |
126 | struct mlx5_hv_vhca_agent *agent; |
127 | |
128 | priv->stats_agent.buf = kvzalloc(size: buf_len, GFP_KERNEL); |
129 | if (!priv->stats_agent.buf) |
130 | return; |
131 | |
132 | agent = mlx5_hv_vhca_agent_create(hv_vhca: priv->mdev->hv_vhca, |
133 | type: MLX5_HV_VHCA_AGENT_STATS, |
134 | control: mlx5e_hv_vhca_stats_control, NULL, |
135 | cleanup: mlx5e_hv_vhca_stats_cleanup, |
136 | context: priv); |
137 | |
138 | if (IS_ERR_OR_NULL(ptr: agent)) { |
139 | if (IS_ERR(ptr: agent)) |
140 | netdev_warn(dev: priv->netdev, |
141 | format: "Failed to create hv vhca stats agent, err = %ld\n" , |
142 | PTR_ERR(ptr: agent)); |
143 | |
144 | kvfree(addr: priv->stats_agent.buf); |
145 | return; |
146 | } |
147 | |
148 | priv->stats_agent.agent = agent; |
149 | INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); |
150 | } |
151 | |
152 | void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv) |
153 | { |
154 | if (IS_ERR_OR_NULL(ptr: priv->stats_agent.agent)) |
155 | return; |
156 | |
157 | mlx5_hv_vhca_agent_destroy(agent: priv->stats_agent.agent); |
158 | kvfree(addr: priv->stats_agent.buf); |
159 | } |
160 | |