1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/in.h> |
3 | #include <linux/inet.h> |
4 | #include <linux/list.h> |
5 | #include <linux/module.h> |
6 | #include <linux/net.h> |
7 | #include <linux/proc_fs.h> |
8 | #include <linux/rculist.h> |
9 | #include <linux/seq_file.h> |
10 | #include <linux/socket.h> |
11 | #include <net/inet_sock.h> |
12 | #include <net/kcm.h> |
13 | #include <net/net_namespace.h> |
14 | #include <net/netns/generic.h> |
15 | #include <net/tcp.h> |
16 | |
17 | #ifdef CONFIG_PROC_FS |
18 | static struct kcm_mux *kcm_get_first(struct seq_file *seq) |
19 | { |
20 | struct net *net = seq_file_net(seq); |
21 | struct kcm_net *knet = net_generic(net, id: kcm_net_id); |
22 | |
23 | return list_first_or_null_rcu(&knet->mux_list, |
24 | struct kcm_mux, kcm_mux_list); |
25 | } |
26 | |
27 | static struct kcm_mux *kcm_get_next(struct kcm_mux *mux) |
28 | { |
29 | struct kcm_net *knet = mux->knet; |
30 | |
31 | return list_next_or_null_rcu(&knet->mux_list, &mux->kcm_mux_list, |
32 | struct kcm_mux, kcm_mux_list); |
33 | } |
34 | |
35 | static struct kcm_mux *kcm_get_idx(struct seq_file *seq, loff_t pos) |
36 | { |
37 | struct net *net = seq_file_net(seq); |
38 | struct kcm_net *knet = net_generic(net, id: kcm_net_id); |
39 | struct kcm_mux *m; |
40 | |
41 | list_for_each_entry_rcu(m, &knet->mux_list, kcm_mux_list) { |
42 | if (!pos) |
43 | return m; |
44 | --pos; |
45 | } |
46 | return NULL; |
47 | } |
48 | |
49 | static void *kcm_seq_next(struct seq_file *seq, void *v, loff_t *pos) |
50 | { |
51 | void *p; |
52 | |
53 | if (v == SEQ_START_TOKEN) |
54 | p = kcm_get_first(seq); |
55 | else |
56 | p = kcm_get_next(mux: v); |
57 | ++*pos; |
58 | return p; |
59 | } |
60 | |
61 | static void *kcm_seq_start(struct seq_file *seq, loff_t *pos) |
62 | __acquires(rcu) |
63 | { |
64 | rcu_read_lock(); |
65 | |
66 | if (!*pos) |
67 | return SEQ_START_TOKEN; |
68 | else |
69 | return kcm_get_idx(seq, pos: *pos - 1); |
70 | } |
71 | |
72 | static void kcm_seq_stop(struct seq_file *seq, void *v) |
73 | __releases(rcu) |
74 | { |
75 | rcu_read_unlock(); |
76 | } |
77 | |
78 | struct kcm_proc_mux_state { |
79 | struct seq_net_private p; |
80 | int idx; |
81 | }; |
82 | |
83 | static void (struct seq_file *seq) |
84 | { |
85 | struct net *net = seq_file_net(seq); |
86 | struct kcm_net *knet = net_generic(net, id: kcm_net_id); |
87 | |
88 | seq_printf(m: seq, |
89 | fmt: "*** KCM statistics (%d MUX) ****\n" , |
90 | knet->count); |
91 | |
92 | seq_printf(m: seq, |
93 | fmt: "%-14s %-10s %-16s %-10s %-16s %-8s %-8s %-8s %-8s %s" , |
94 | "Object" , |
95 | "RX-Msgs" , |
96 | "RX-Bytes" , |
97 | "TX-Msgs" , |
98 | "TX-Bytes" , |
99 | "Recv-Q" , |
100 | "Rmem" , |
101 | "Send-Q" , |
102 | "Smem" , |
103 | "Status" ); |
104 | |
105 | /* XXX: pdsts header stuff here */ |
106 | seq_puts(m: seq, s: "\n" ); |
107 | } |
108 | |
109 | static void kcm_format_sock(struct kcm_sock *kcm, struct seq_file *seq, |
110 | int i, int *len) |
111 | { |
112 | seq_printf(m: seq, |
113 | fmt: " kcm-%-7u %-10llu %-16llu %-10llu %-16llu %-8d %-8d %-8d %-8s " , |
114 | kcm->index, |
115 | kcm->stats.rx_msgs, |
116 | kcm->stats.rx_bytes, |
117 | kcm->stats.tx_msgs, |
118 | kcm->stats.tx_bytes, |
119 | kcm->sk.sk_receive_queue.qlen, |
120 | sk_rmem_alloc_get(sk: &kcm->sk), |
121 | kcm->sk.sk_write_queue.qlen, |
122 | "-" ); |
123 | |
124 | if (kcm->tx_psock) |
125 | seq_printf(m: seq, fmt: "Psck-%u " , kcm->tx_psock->index); |
126 | |
127 | if (kcm->tx_wait) |
128 | seq_puts(m: seq, s: "TxWait " ); |
129 | |
130 | if (kcm->tx_wait_more) |
131 | seq_puts(m: seq, s: "WMore " ); |
132 | |
133 | if (kcm->rx_wait) |
134 | seq_puts(m: seq, s: "RxWait " ); |
135 | |
136 | seq_puts(m: seq, s: "\n" ); |
137 | } |
138 | |
139 | static void kcm_format_psock(struct kcm_psock *psock, struct seq_file *seq, |
140 | int i, int *len) |
141 | { |
142 | seq_printf(m: seq, |
143 | fmt: " psock-%-5u %-10llu %-16llu %-10llu %-16llu %-8d %-8d %-8d %-8d " , |
144 | psock->index, |
145 | psock->strp.stats.msgs, |
146 | psock->strp.stats.bytes, |
147 | psock->stats.tx_msgs, |
148 | psock->stats.tx_bytes, |
149 | psock->sk->sk_receive_queue.qlen, |
150 | atomic_read(v: &psock->sk->sk_rmem_alloc), |
151 | psock->sk->sk_write_queue.qlen, |
152 | refcount_read(r: &psock->sk->sk_wmem_alloc)); |
153 | |
154 | if (psock->done) |
155 | seq_puts(m: seq, s: "Done " ); |
156 | |
157 | if (psock->tx_stopped) |
158 | seq_puts(m: seq, s: "TxStop " ); |
159 | |
160 | if (psock->strp.stopped) |
161 | seq_puts(m: seq, s: "RxStop " ); |
162 | |
163 | if (psock->tx_kcm) |
164 | seq_printf(m: seq, fmt: "Rsvd-%d " , psock->tx_kcm->index); |
165 | |
166 | if (!psock->strp.paused && !psock->ready_rx_msg) { |
167 | if (psock->sk->sk_receive_queue.qlen) { |
168 | if (psock->strp.need_bytes) |
169 | seq_printf(m: seq, fmt: "RxWait=%u " , |
170 | psock->strp.need_bytes); |
171 | else |
172 | seq_printf(m: seq, fmt: "RxWait " ); |
173 | } |
174 | } else { |
175 | if (psock->strp.paused) |
176 | seq_puts(m: seq, s: "RxPause " ); |
177 | |
178 | if (psock->ready_rx_msg) |
179 | seq_puts(m: seq, s: "RdyRx " ); |
180 | } |
181 | |
182 | seq_puts(m: seq, s: "\n" ); |
183 | } |
184 | |
185 | static void |
186 | kcm_format_mux(struct kcm_mux *mux, loff_t idx, struct seq_file *seq) |
187 | { |
188 | int i, len; |
189 | struct kcm_sock *kcm; |
190 | struct kcm_psock *psock; |
191 | |
192 | /* mux information */ |
193 | seq_printf(m: seq, |
194 | fmt: "%-6s%-8s %-10llu %-16llu %-10llu %-16llu %-8s %-8s %-8s %-8s " , |
195 | "mux" , "" , |
196 | mux->stats.rx_msgs, |
197 | mux->stats.rx_bytes, |
198 | mux->stats.tx_msgs, |
199 | mux->stats.tx_bytes, |
200 | "-" , "-" , "-" , "-" ); |
201 | |
202 | seq_printf(m: seq, fmt: "KCMs: %d, Psocks %d\n" , |
203 | mux->kcm_socks_cnt, mux->psocks_cnt); |
204 | |
205 | /* kcm sock information */ |
206 | i = 0; |
207 | spin_lock_bh(lock: &mux->lock); |
208 | list_for_each_entry(kcm, &mux->kcm_socks, kcm_sock_list) { |
209 | kcm_format_sock(kcm, seq, i, len: &len); |
210 | i++; |
211 | } |
212 | i = 0; |
213 | list_for_each_entry(psock, &mux->psocks, psock_list) { |
214 | kcm_format_psock(psock, seq, i, len: &len); |
215 | i++; |
216 | } |
217 | spin_unlock_bh(lock: &mux->lock); |
218 | } |
219 | |
220 | static int kcm_seq_show(struct seq_file *seq, void *v) |
221 | { |
222 | struct kcm_proc_mux_state *mux_state; |
223 | |
224 | mux_state = seq->private; |
225 | if (v == SEQ_START_TOKEN) { |
226 | mux_state->idx = 0; |
227 | kcm_format_mux_header(seq); |
228 | } else { |
229 | kcm_format_mux(mux: v, idx: mux_state->idx, seq); |
230 | mux_state->idx++; |
231 | } |
232 | return 0; |
233 | } |
234 | |
235 | static const struct seq_operations kcm_seq_ops = { |
236 | .show = kcm_seq_show, |
237 | .start = kcm_seq_start, |
238 | .next = kcm_seq_next, |
239 | .stop = kcm_seq_stop, |
240 | }; |
241 | |
242 | static int kcm_stats_seq_show(struct seq_file *seq, void *v) |
243 | { |
244 | struct kcm_psock_stats psock_stats; |
245 | struct kcm_mux_stats mux_stats; |
246 | struct strp_aggr_stats strp_stats; |
247 | struct kcm_mux *mux; |
248 | struct kcm_psock *psock; |
249 | struct net *net = seq->private; |
250 | struct kcm_net *knet = net_generic(net, id: kcm_net_id); |
251 | |
252 | memset(&mux_stats, 0, sizeof(mux_stats)); |
253 | memset(&psock_stats, 0, sizeof(psock_stats)); |
254 | memset(&strp_stats, 0, sizeof(strp_stats)); |
255 | |
256 | mutex_lock(&knet->mutex); |
257 | |
258 | aggregate_mux_stats(stats: &knet->aggregate_mux_stats, agg_stats: &mux_stats); |
259 | aggregate_psock_stats(stats: &knet->aggregate_psock_stats, |
260 | agg_stats: &psock_stats); |
261 | aggregate_strp_stats(stats: &knet->aggregate_strp_stats, |
262 | agg_stats: &strp_stats); |
263 | |
264 | list_for_each_entry(mux, &knet->mux_list, kcm_mux_list) { |
265 | spin_lock_bh(lock: &mux->lock); |
266 | aggregate_mux_stats(stats: &mux->stats, agg_stats: &mux_stats); |
267 | aggregate_psock_stats(stats: &mux->aggregate_psock_stats, |
268 | agg_stats: &psock_stats); |
269 | aggregate_strp_stats(stats: &mux->aggregate_strp_stats, |
270 | agg_stats: &strp_stats); |
271 | list_for_each_entry(psock, &mux->psocks, psock_list) { |
272 | aggregate_psock_stats(stats: &psock->stats, agg_stats: &psock_stats); |
273 | save_strp_stats(strp: &psock->strp, agg_stats: &strp_stats); |
274 | } |
275 | |
276 | spin_unlock_bh(lock: &mux->lock); |
277 | } |
278 | |
279 | mutex_unlock(lock: &knet->mutex); |
280 | |
281 | seq_printf(m: seq, |
282 | fmt: "%-8s %-10s %-16s %-10s %-16s %-10s %-10s %-10s %-10s %-10s\n" , |
283 | "MUX" , |
284 | "RX-Msgs" , |
285 | "RX-Bytes" , |
286 | "TX-Msgs" , |
287 | "TX-Bytes" , |
288 | "TX-Retries" , |
289 | "Attach" , |
290 | "Unattach" , |
291 | "UnattchRsvd" , |
292 | "RX-RdyDrops" ); |
293 | |
294 | seq_printf(m: seq, |
295 | fmt: "%-8s %-10llu %-16llu %-10llu %-16llu %-10u %-10u %-10u %-10u %-10u\n" , |
296 | "" , |
297 | mux_stats.rx_msgs, |
298 | mux_stats.rx_bytes, |
299 | mux_stats.tx_msgs, |
300 | mux_stats.tx_bytes, |
301 | mux_stats.tx_retries, |
302 | mux_stats.psock_attach, |
303 | mux_stats.psock_unattach_rsvd, |
304 | mux_stats.psock_unattach, |
305 | mux_stats.rx_ready_drops); |
306 | |
307 | seq_printf(m: seq, |
308 | fmt: "%-8s %-10s %-16s %-10s %-16s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s\n" , |
309 | "Psock" , |
310 | "RX-Msgs" , |
311 | "RX-Bytes" , |
312 | "TX-Msgs" , |
313 | "TX-Bytes" , |
314 | "Reserved" , |
315 | "Unreserved" , |
316 | "RX-Aborts" , |
317 | "RX-Intr" , |
318 | "RX-Unrecov" , |
319 | "RX-MemFail" , |
320 | "RX-NeedMor" , |
321 | "RX-BadLen" , |
322 | "RX-TooBig" , |
323 | "RX-Timeout" , |
324 | "TX-Aborts" ); |
325 | |
326 | seq_printf(m: seq, |
327 | fmt: "%-8s %-10llu %-16llu %-10llu %-16llu %-10llu %-10llu %-10u %-10u %-10u %-10u %-10u %-10u %-10u %-10u %-10u\n" , |
328 | "" , |
329 | strp_stats.msgs, |
330 | strp_stats.bytes, |
331 | psock_stats.tx_msgs, |
332 | psock_stats.tx_bytes, |
333 | psock_stats.reserved, |
334 | psock_stats.unreserved, |
335 | strp_stats.aborts, |
336 | strp_stats.interrupted, |
337 | strp_stats.unrecov_intr, |
338 | strp_stats.mem_fail, |
339 | strp_stats.need_more_hdr, |
340 | strp_stats.bad_hdr_len, |
341 | strp_stats.msg_too_big, |
342 | strp_stats.msg_timeouts, |
343 | psock_stats.tx_aborts); |
344 | |
345 | return 0; |
346 | } |
347 | |
348 | static int kcm_proc_init_net(struct net *net) |
349 | { |
350 | if (!proc_create_net_single(name: "kcm_stats" , mode: 0444, parent: net->proc_net, |
351 | show: kcm_stats_seq_show, NULL)) |
352 | goto out_kcm_stats; |
353 | |
354 | if (!proc_create_net("kcm" , 0444, net->proc_net, &kcm_seq_ops, |
355 | sizeof(struct kcm_proc_mux_state))) |
356 | goto out_kcm; |
357 | |
358 | return 0; |
359 | |
360 | out_kcm: |
361 | remove_proc_entry("kcm_stats" , net->proc_net); |
362 | out_kcm_stats: |
363 | return -ENOMEM; |
364 | } |
365 | |
366 | static void kcm_proc_exit_net(struct net *net) |
367 | { |
368 | remove_proc_entry("kcm" , net->proc_net); |
369 | remove_proc_entry("kcm_stats" , net->proc_net); |
370 | } |
371 | |
372 | static struct pernet_operations kcm_net_ops = { |
373 | .init = kcm_proc_init_net, |
374 | .exit = kcm_proc_exit_net, |
375 | }; |
376 | |
377 | int __init kcm_proc_init(void) |
378 | { |
379 | return register_pernet_subsys(&kcm_net_ops); |
380 | } |
381 | |
382 | void __exit kcm_proc_exit(void) |
383 | { |
384 | unregister_pernet_subsys(&kcm_net_ops); |
385 | } |
386 | |
387 | #endif /* CONFIG_PROC_FS */ |
388 | |