1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
2 | /* |
3 | * proc.c - procfs support for Protocol family CAN core module |
4 | * |
5 | * Copyright (c) 2002-2007 Volkswagen Group Electronic Research |
6 | * All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * 3. Neither the name of Volkswagen nor the names of its contributors |
17 | * may be used to endorse or promote products derived from this software |
18 | * without specific prior written permission. |
19 | * |
20 | * Alternatively, provided that this notice is retained in full, this |
21 | * software may be distributed under the terms of the GNU General |
22 | * Public License ("GPL") version 2, in which case the provisions of the |
23 | * GPL apply INSTEAD OF those given above. |
24 | * |
25 | * The provided data structures and external interfaces from this code |
26 | * are not restricted to be used by modules with a GPL compatible license. |
27 | * |
28 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
29 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
31 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
32 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
33 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
34 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
35 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
36 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
37 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
38 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
39 | * DAMAGE. |
40 | * |
41 | */ |
42 | |
43 | #include <linux/module.h> |
44 | #include <linux/proc_fs.h> |
45 | #include <linux/list.h> |
46 | #include <linux/rcupdate.h> |
47 | #include <linux/if_arp.h> |
48 | #include <linux/can/can-ml.h> |
49 | #include <linux/can/core.h> |
50 | |
51 | #include "af_can.h" |
52 | |
53 | /* |
54 | * proc filenames for the PF_CAN core |
55 | */ |
56 | |
57 | #define CAN_PROC_STATS "stats" |
58 | #define CAN_PROC_RESET_STATS "reset_stats" |
59 | #define CAN_PROC_RCVLIST_ALL "rcvlist_all" |
60 | #define CAN_PROC_RCVLIST_FIL "rcvlist_fil" |
61 | #define CAN_PROC_RCVLIST_INV "rcvlist_inv" |
62 | #define CAN_PROC_RCVLIST_SFF "rcvlist_sff" |
63 | #define CAN_PROC_RCVLIST_EFF "rcvlist_eff" |
64 | #define CAN_PROC_RCVLIST_ERR "rcvlist_err" |
65 | |
66 | static int user_reset; |
67 | |
68 | static const char rx_list_name[][8] = { |
69 | [RX_ERR] = "rx_err" , |
70 | [RX_ALL] = "rx_all" , |
71 | [RX_FIL] = "rx_fil" , |
72 | [RX_INV] = "rx_inv" , |
73 | }; |
74 | |
75 | /* |
76 | * af_can statistics stuff |
77 | */ |
78 | |
79 | static void can_init_stats(struct net *net) |
80 | { |
81 | struct can_pkg_stats *pkg_stats = net->can.pkg_stats; |
82 | struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats; |
83 | /* |
84 | * This memset function is called from a timer context (when |
85 | * can_stattimer is active which is the default) OR in a process |
86 | * context (reading the proc_fs when can_stattimer is disabled). |
87 | */ |
88 | memset(pkg_stats, 0, sizeof(struct can_pkg_stats)); |
89 | pkg_stats->jiffies_init = jiffies; |
90 | |
91 | rcv_lists_stats->stats_reset++; |
92 | |
93 | if (user_reset) { |
94 | user_reset = 0; |
95 | rcv_lists_stats->user_reset++; |
96 | } |
97 | } |
98 | |
99 | static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif, |
100 | unsigned long count) |
101 | { |
102 | if (oldjif == newjif) |
103 | return 0; |
104 | |
105 | /* see can_stat_update() - this should NEVER happen! */ |
106 | if (count > (ULONG_MAX / HZ)) { |
107 | printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n" , |
108 | count); |
109 | return 99999999; |
110 | } |
111 | |
112 | return (count * HZ) / (newjif - oldjif); |
113 | } |
114 | |
115 | void can_stat_update(struct timer_list *t) |
116 | { |
117 | struct net *net = from_timer(net, t, can.stattimer); |
118 | struct can_pkg_stats *pkg_stats = net->can.pkg_stats; |
119 | unsigned long j = jiffies; /* snapshot */ |
120 | |
121 | /* restart counting in timer context on user request */ |
122 | if (user_reset) |
123 | can_init_stats(net); |
124 | |
125 | /* restart counting on jiffies overflow */ |
126 | if (j < pkg_stats->jiffies_init) |
127 | can_init_stats(net); |
128 | |
129 | /* prevent overflow in calc_rate() */ |
130 | if (pkg_stats->rx_frames > (ULONG_MAX / HZ)) |
131 | can_init_stats(net); |
132 | |
133 | /* prevent overflow in calc_rate() */ |
134 | if (pkg_stats->tx_frames > (ULONG_MAX / HZ)) |
135 | can_init_stats(net); |
136 | |
137 | /* matches overflow - very improbable */ |
138 | if (pkg_stats->matches > (ULONG_MAX / 100)) |
139 | can_init_stats(net); |
140 | |
141 | /* calc total values */ |
142 | if (pkg_stats->rx_frames) |
143 | pkg_stats->total_rx_match_ratio = (pkg_stats->matches * 100) / |
144 | pkg_stats->rx_frames; |
145 | |
146 | pkg_stats->total_tx_rate = calc_rate(oldjif: pkg_stats->jiffies_init, newjif: j, |
147 | count: pkg_stats->tx_frames); |
148 | pkg_stats->total_rx_rate = calc_rate(oldjif: pkg_stats->jiffies_init, newjif: j, |
149 | count: pkg_stats->rx_frames); |
150 | |
151 | /* calc current values */ |
152 | if (pkg_stats->rx_frames_delta) |
153 | pkg_stats->current_rx_match_ratio = |
154 | (pkg_stats->matches_delta * 100) / |
155 | pkg_stats->rx_frames_delta; |
156 | |
157 | pkg_stats->current_tx_rate = calc_rate(oldjif: 0, HZ, count: pkg_stats->tx_frames_delta); |
158 | pkg_stats->current_rx_rate = calc_rate(oldjif: 0, HZ, count: pkg_stats->rx_frames_delta); |
159 | |
160 | /* check / update maximum values */ |
161 | if (pkg_stats->max_tx_rate < pkg_stats->current_tx_rate) |
162 | pkg_stats->max_tx_rate = pkg_stats->current_tx_rate; |
163 | |
164 | if (pkg_stats->max_rx_rate < pkg_stats->current_rx_rate) |
165 | pkg_stats->max_rx_rate = pkg_stats->current_rx_rate; |
166 | |
167 | if (pkg_stats->max_rx_match_ratio < pkg_stats->current_rx_match_ratio) |
168 | pkg_stats->max_rx_match_ratio = pkg_stats->current_rx_match_ratio; |
169 | |
170 | /* clear values for 'current rate' calculation */ |
171 | pkg_stats->tx_frames_delta = 0; |
172 | pkg_stats->rx_frames_delta = 0; |
173 | pkg_stats->matches_delta = 0; |
174 | |
175 | /* restart timer (one second) */ |
176 | mod_timer(timer: &net->can.stattimer, expires: round_jiffies(j: jiffies + HZ)); |
177 | } |
178 | |
179 | /* |
180 | * proc read functions |
181 | */ |
182 | |
183 | static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list, |
184 | struct net_device *dev) |
185 | { |
186 | struct receiver *r; |
187 | |
188 | hlist_for_each_entry_rcu(r, rx_list, list) { |
189 | char *fmt = (r->can_id & CAN_EFF_FLAG)? |
190 | " %-5s %08x %08x %pK %pK %8ld %s\n" : |
191 | " %-5s %03x %08x %pK %pK %8ld %s\n" ; |
192 | |
193 | seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask, |
194 | r->func, r->data, r->matches, r->ident); |
195 | } |
196 | } |
197 | |
198 | static void can_print_recv_banner(struct seq_file *m) |
199 | { |
200 | /* |
201 | * can1. 00000000 00000000 00000000 |
202 | * ....... 0 tp20 |
203 | */ |
204 | if (IS_ENABLED(CONFIG_64BIT)) |
205 | seq_puts(m, s: " device can_id can_mask function userdata matches ident\n" ); |
206 | else |
207 | seq_puts(m, s: " device can_id can_mask function userdata matches ident\n" ); |
208 | } |
209 | |
210 | static int can_stats_proc_show(struct seq_file *m, void *v) |
211 | { |
212 | struct net *net = m->private; |
213 | struct can_pkg_stats *pkg_stats = net->can.pkg_stats; |
214 | struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats; |
215 | |
216 | seq_putc(m, c: '\n'); |
217 | seq_printf(m, fmt: " %8ld transmitted frames (TXF)\n" , pkg_stats->tx_frames); |
218 | seq_printf(m, fmt: " %8ld received frames (RXF)\n" , pkg_stats->rx_frames); |
219 | seq_printf(m, fmt: " %8ld matched frames (RXMF)\n" , pkg_stats->matches); |
220 | |
221 | seq_putc(m, c: '\n'); |
222 | |
223 | if (net->can.stattimer.function == can_stat_update) { |
224 | seq_printf(m, fmt: " %8ld %% total match ratio (RXMR)\n" , |
225 | pkg_stats->total_rx_match_ratio); |
226 | |
227 | seq_printf(m, fmt: " %8ld frames/s total tx rate (TXR)\n" , |
228 | pkg_stats->total_tx_rate); |
229 | seq_printf(m, fmt: " %8ld frames/s total rx rate (RXR)\n" , |
230 | pkg_stats->total_rx_rate); |
231 | |
232 | seq_putc(m, c: '\n'); |
233 | |
234 | seq_printf(m, fmt: " %8ld %% current match ratio (CRXMR)\n" , |
235 | pkg_stats->current_rx_match_ratio); |
236 | |
237 | seq_printf(m, fmt: " %8ld frames/s current tx rate (CTXR)\n" , |
238 | pkg_stats->current_tx_rate); |
239 | seq_printf(m, fmt: " %8ld frames/s current rx rate (CRXR)\n" , |
240 | pkg_stats->current_rx_rate); |
241 | |
242 | seq_putc(m, c: '\n'); |
243 | |
244 | seq_printf(m, fmt: " %8ld %% max match ratio (MRXMR)\n" , |
245 | pkg_stats->max_rx_match_ratio); |
246 | |
247 | seq_printf(m, fmt: " %8ld frames/s max tx rate (MTXR)\n" , |
248 | pkg_stats->max_tx_rate); |
249 | seq_printf(m, fmt: " %8ld frames/s max rx rate (MRXR)\n" , |
250 | pkg_stats->max_rx_rate); |
251 | |
252 | seq_putc(m, c: '\n'); |
253 | } |
254 | |
255 | seq_printf(m, fmt: " %8ld current receive list entries (CRCV)\n" , |
256 | rcv_lists_stats->rcv_entries); |
257 | seq_printf(m, fmt: " %8ld maximum receive list entries (MRCV)\n" , |
258 | rcv_lists_stats->rcv_entries_max); |
259 | |
260 | if (rcv_lists_stats->stats_reset) |
261 | seq_printf(m, fmt: "\n %8ld statistic resets (STR)\n" , |
262 | rcv_lists_stats->stats_reset); |
263 | |
264 | if (rcv_lists_stats->user_reset) |
265 | seq_printf(m, fmt: " %8ld user statistic resets (USTR)\n" , |
266 | rcv_lists_stats->user_reset); |
267 | |
268 | seq_putc(m, c: '\n'); |
269 | return 0; |
270 | } |
271 | |
272 | static int can_reset_stats_proc_show(struct seq_file *m, void *v) |
273 | { |
274 | struct net *net = m->private; |
275 | struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats; |
276 | struct can_pkg_stats *pkg_stats = net->can.pkg_stats; |
277 | |
278 | user_reset = 1; |
279 | |
280 | if (net->can.stattimer.function == can_stat_update) { |
281 | seq_printf(m, fmt: "Scheduled statistic reset #%ld.\n" , |
282 | rcv_lists_stats->stats_reset + 1); |
283 | } else { |
284 | if (pkg_stats->jiffies_init != jiffies) |
285 | can_init_stats(net); |
286 | |
287 | seq_printf(m, fmt: "Performed statistic reset #%ld.\n" , |
288 | rcv_lists_stats->stats_reset); |
289 | } |
290 | return 0; |
291 | } |
292 | |
293 | static inline void can_rcvlist_proc_show_one(struct seq_file *m, int idx, |
294 | struct net_device *dev, |
295 | struct can_dev_rcv_lists *dev_rcv_lists) |
296 | { |
297 | if (!hlist_empty(h: &dev_rcv_lists->rx[idx])) { |
298 | can_print_recv_banner(m); |
299 | can_print_rcvlist(m, rx_list: &dev_rcv_lists->rx[idx], dev); |
300 | } else |
301 | seq_printf(m, fmt: " (%s: no entry)\n" , DNAME(dev)); |
302 | |
303 | } |
304 | |
305 | static int can_rcvlist_proc_show(struct seq_file *m, void *v) |
306 | { |
307 | /* double cast to prevent GCC warning */ |
308 | int idx = (int)(long)pde_data(inode: m->file->f_inode); |
309 | struct net_device *dev; |
310 | struct can_dev_rcv_lists *dev_rcv_lists; |
311 | struct net *net = m->private; |
312 | |
313 | seq_printf(m, fmt: "\nreceive list '%s':\n" , rx_list_name[idx]); |
314 | |
315 | rcu_read_lock(); |
316 | |
317 | /* receive list for 'all' CAN devices (dev == NULL) */ |
318 | dev_rcv_lists = net->can.rx_alldev_list; |
319 | can_rcvlist_proc_show_one(m, idx, NULL, dev_rcv_lists); |
320 | |
321 | /* receive list for registered CAN devices */ |
322 | for_each_netdev_rcu(net, dev) { |
323 | struct can_ml_priv *can_ml = can_get_ml_priv(dev); |
324 | |
325 | if (can_ml) |
326 | can_rcvlist_proc_show_one(m, idx, dev, |
327 | dev_rcv_lists: &can_ml->dev_rcv_lists); |
328 | } |
329 | |
330 | rcu_read_unlock(); |
331 | |
332 | seq_putc(m, c: '\n'); |
333 | return 0; |
334 | } |
335 | |
336 | static inline void can_rcvlist_proc_show_array(struct seq_file *m, |
337 | struct net_device *dev, |
338 | struct hlist_head *rcv_array, |
339 | unsigned int rcv_array_sz) |
340 | { |
341 | unsigned int i; |
342 | int all_empty = 1; |
343 | |
344 | /* check whether at least one list is non-empty */ |
345 | for (i = 0; i < rcv_array_sz; i++) |
346 | if (!hlist_empty(h: &rcv_array[i])) { |
347 | all_empty = 0; |
348 | break; |
349 | } |
350 | |
351 | if (!all_empty) { |
352 | can_print_recv_banner(m); |
353 | for (i = 0; i < rcv_array_sz; i++) { |
354 | if (!hlist_empty(h: &rcv_array[i])) |
355 | can_print_rcvlist(m, rx_list: &rcv_array[i], dev); |
356 | } |
357 | } else |
358 | seq_printf(m, fmt: " (%s: no entry)\n" , DNAME(dev)); |
359 | } |
360 | |
361 | static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v) |
362 | { |
363 | struct net_device *dev; |
364 | struct can_dev_rcv_lists *dev_rcv_lists; |
365 | struct net *net = m->private; |
366 | |
367 | /* RX_SFF */ |
368 | seq_puts(m, s: "\nreceive list 'rx_sff':\n" ); |
369 | |
370 | rcu_read_lock(); |
371 | |
372 | /* sff receive list for 'all' CAN devices (dev == NULL) */ |
373 | dev_rcv_lists = net->can.rx_alldev_list; |
374 | can_rcvlist_proc_show_array(m, NULL, rcv_array: dev_rcv_lists->rx_sff, |
375 | ARRAY_SIZE(dev_rcv_lists->rx_sff)); |
376 | |
377 | /* sff receive list for registered CAN devices */ |
378 | for_each_netdev_rcu(net, dev) { |
379 | struct can_ml_priv *can_ml = can_get_ml_priv(dev); |
380 | |
381 | if (can_ml) { |
382 | dev_rcv_lists = &can_ml->dev_rcv_lists; |
383 | can_rcvlist_proc_show_array(m, dev, rcv_array: dev_rcv_lists->rx_sff, |
384 | ARRAY_SIZE(dev_rcv_lists->rx_sff)); |
385 | } |
386 | } |
387 | |
388 | rcu_read_unlock(); |
389 | |
390 | seq_putc(m, c: '\n'); |
391 | return 0; |
392 | } |
393 | |
394 | static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v) |
395 | { |
396 | struct net_device *dev; |
397 | struct can_dev_rcv_lists *dev_rcv_lists; |
398 | struct net *net = m->private; |
399 | |
400 | /* RX_EFF */ |
401 | seq_puts(m, s: "\nreceive list 'rx_eff':\n" ); |
402 | |
403 | rcu_read_lock(); |
404 | |
405 | /* eff receive list for 'all' CAN devices (dev == NULL) */ |
406 | dev_rcv_lists = net->can.rx_alldev_list; |
407 | can_rcvlist_proc_show_array(m, NULL, rcv_array: dev_rcv_lists->rx_eff, |
408 | ARRAY_SIZE(dev_rcv_lists->rx_eff)); |
409 | |
410 | /* eff receive list for registered CAN devices */ |
411 | for_each_netdev_rcu(net, dev) { |
412 | struct can_ml_priv *can_ml = can_get_ml_priv(dev); |
413 | |
414 | if (can_ml) { |
415 | dev_rcv_lists = &can_ml->dev_rcv_lists; |
416 | can_rcvlist_proc_show_array(m, dev, rcv_array: dev_rcv_lists->rx_eff, |
417 | ARRAY_SIZE(dev_rcv_lists->rx_eff)); |
418 | } |
419 | } |
420 | |
421 | rcu_read_unlock(); |
422 | |
423 | seq_putc(m, c: '\n'); |
424 | return 0; |
425 | } |
426 | |
427 | /* |
428 | * can_init_proc - create main CAN proc directory and procfs entries |
429 | */ |
430 | void can_init_proc(struct net *net) |
431 | { |
432 | /* create /proc/net/can directory */ |
433 | net->can.proc_dir = proc_net_mkdir(net, name: "can" , parent: net->proc_net); |
434 | |
435 | if (!net->can.proc_dir) { |
436 | printk(KERN_INFO "can: failed to create /proc/net/can . " |
437 | "CONFIG_PROC_FS missing?\n" ); |
438 | return; |
439 | } |
440 | |
441 | /* own procfs entries from the AF_CAN core */ |
442 | net->can.pde_stats = proc_create_net_single(CAN_PROC_STATS, mode: 0644, |
443 | parent: net->can.proc_dir, show: can_stats_proc_show, NULL); |
444 | net->can.pde_reset_stats = proc_create_net_single(CAN_PROC_RESET_STATS, |
445 | mode: 0644, parent: net->can.proc_dir, show: can_reset_stats_proc_show, |
446 | NULL); |
447 | net->can.pde_rcvlist_err = proc_create_net_single(CAN_PROC_RCVLIST_ERR, |
448 | mode: 0644, parent: net->can.proc_dir, show: can_rcvlist_proc_show, |
449 | data: (void *)RX_ERR); |
450 | net->can.pde_rcvlist_all = proc_create_net_single(CAN_PROC_RCVLIST_ALL, |
451 | mode: 0644, parent: net->can.proc_dir, show: can_rcvlist_proc_show, |
452 | data: (void *)RX_ALL); |
453 | net->can.pde_rcvlist_fil = proc_create_net_single(CAN_PROC_RCVLIST_FIL, |
454 | mode: 0644, parent: net->can.proc_dir, show: can_rcvlist_proc_show, |
455 | data: (void *)RX_FIL); |
456 | net->can.pde_rcvlist_inv = proc_create_net_single(CAN_PROC_RCVLIST_INV, |
457 | mode: 0644, parent: net->can.proc_dir, show: can_rcvlist_proc_show, |
458 | data: (void *)RX_INV); |
459 | net->can.pde_rcvlist_eff = proc_create_net_single(CAN_PROC_RCVLIST_EFF, |
460 | mode: 0644, parent: net->can.proc_dir, show: can_rcvlist_eff_proc_show, NULL); |
461 | net->can.pde_rcvlist_sff = proc_create_net_single(CAN_PROC_RCVLIST_SFF, |
462 | mode: 0644, parent: net->can.proc_dir, show: can_rcvlist_sff_proc_show, NULL); |
463 | } |
464 | |
465 | /* |
466 | * can_remove_proc - remove procfs entries and main CAN proc directory |
467 | */ |
468 | void can_remove_proc(struct net *net) |
469 | { |
470 | if (!net->can.proc_dir) |
471 | return; |
472 | |
473 | if (net->can.pde_stats) |
474 | remove_proc_entry(CAN_PROC_STATS, net->can.proc_dir); |
475 | |
476 | if (net->can.pde_reset_stats) |
477 | remove_proc_entry(CAN_PROC_RESET_STATS, net->can.proc_dir); |
478 | |
479 | if (net->can.pde_rcvlist_err) |
480 | remove_proc_entry(CAN_PROC_RCVLIST_ERR, net->can.proc_dir); |
481 | |
482 | if (net->can.pde_rcvlist_all) |
483 | remove_proc_entry(CAN_PROC_RCVLIST_ALL, net->can.proc_dir); |
484 | |
485 | if (net->can.pde_rcvlist_fil) |
486 | remove_proc_entry(CAN_PROC_RCVLIST_FIL, net->can.proc_dir); |
487 | |
488 | if (net->can.pde_rcvlist_inv) |
489 | remove_proc_entry(CAN_PROC_RCVLIST_INV, net->can.proc_dir); |
490 | |
491 | if (net->can.pde_rcvlist_eff) |
492 | remove_proc_entry(CAN_PROC_RCVLIST_EFF, net->can.proc_dir); |
493 | |
494 | if (net->can.pde_rcvlist_sff) |
495 | remove_proc_entry(CAN_PROC_RCVLIST_SFF, net->can.proc_dir); |
496 | |
497 | remove_proc_entry("can" , net->proc_net); |
498 | } |
499 | |