18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * proc.c - procfs support for Protocol family CAN core module 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2002-2007 Volkswagen Group Electronic Research 68c2ecf20Sopenharmony_ci * All rights reserved. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 98c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 108c2ecf20Sopenharmony_ci * are met: 118c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 128c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 138c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 148c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 158c2ecf20Sopenharmony_ci * documentation and/or other materials provided with the distribution. 168c2ecf20Sopenharmony_ci * 3. Neither the name of Volkswagen nor the names of its contributors 178c2ecf20Sopenharmony_ci * may be used to endorse or promote products derived from this software 188c2ecf20Sopenharmony_ci * without specific prior written permission. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this 218c2ecf20Sopenharmony_ci * software may be distributed under the terms of the GNU General 228c2ecf20Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the 238c2ecf20Sopenharmony_ci * GPL apply INSTEAD OF those given above. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * The provided data structures and external interfaces from this code 268c2ecf20Sopenharmony_ci * are not restricted to be used by modules with a GPL compatible license. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 298c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 308c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 318c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 328c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 338c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 348c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 358c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 368c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 378c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 388c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 398c2ecf20Sopenharmony_ci * DAMAGE. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include <linux/module.h> 448c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 458c2ecf20Sopenharmony_ci#include <linux/list.h> 468c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 478c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 488c2ecf20Sopenharmony_ci#include <linux/can/can-ml.h> 498c2ecf20Sopenharmony_ci#include <linux/can/core.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include "af_can.h" 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * proc filenames for the PF_CAN core 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define CAN_PROC_STATS "stats" 588c2ecf20Sopenharmony_ci#define CAN_PROC_RESET_STATS "reset_stats" 598c2ecf20Sopenharmony_ci#define CAN_PROC_RCVLIST_ALL "rcvlist_all" 608c2ecf20Sopenharmony_ci#define CAN_PROC_RCVLIST_FIL "rcvlist_fil" 618c2ecf20Sopenharmony_ci#define CAN_PROC_RCVLIST_INV "rcvlist_inv" 628c2ecf20Sopenharmony_ci#define CAN_PROC_RCVLIST_SFF "rcvlist_sff" 638c2ecf20Sopenharmony_ci#define CAN_PROC_RCVLIST_EFF "rcvlist_eff" 648c2ecf20Sopenharmony_ci#define CAN_PROC_RCVLIST_ERR "rcvlist_err" 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int user_reset; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic const char rx_list_name[][8] = { 698c2ecf20Sopenharmony_ci [RX_ERR] = "rx_err", 708c2ecf20Sopenharmony_ci [RX_ALL] = "rx_all", 718c2ecf20Sopenharmony_ci [RX_FIL] = "rx_fil", 728c2ecf20Sopenharmony_ci [RX_INV] = "rx_inv", 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * af_can statistics stuff 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void can_init_stats(struct net *net) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct can_pkg_stats *pkg_stats = net->can.pkg_stats; 828c2ecf20Sopenharmony_ci struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats; 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * This memset function is called from a timer context (when 858c2ecf20Sopenharmony_ci * can_stattimer is active which is the default) OR in a process 868c2ecf20Sopenharmony_ci * context (reading the proc_fs when can_stattimer is disabled). 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci memset(pkg_stats, 0, sizeof(struct can_pkg_stats)); 898c2ecf20Sopenharmony_ci pkg_stats->jiffies_init = jiffies; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci rcv_lists_stats->stats_reset++; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (user_reset) { 948c2ecf20Sopenharmony_ci user_reset = 0; 958c2ecf20Sopenharmony_ci rcv_lists_stats->user_reset++; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic unsigned long calc_rate(unsigned long oldjif, unsigned long newjif, 1008c2ecf20Sopenharmony_ci unsigned long count) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci unsigned long rate; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (oldjif == newjif) 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* see can_stat_update() - this should NEVER happen! */ 1088c2ecf20Sopenharmony_ci if (count > (ULONG_MAX / HZ)) { 1098c2ecf20Sopenharmony_ci printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n", 1108c2ecf20Sopenharmony_ci count); 1118c2ecf20Sopenharmony_ci return 99999999; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci rate = (count * HZ) / (newjif - oldjif); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return rate; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_civoid can_stat_update(struct timer_list *t) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct net *net = from_timer(net, t, can.stattimer); 1228c2ecf20Sopenharmony_ci struct can_pkg_stats *pkg_stats = net->can.pkg_stats; 1238c2ecf20Sopenharmony_ci unsigned long j = jiffies; /* snapshot */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* restart counting in timer context on user request */ 1268c2ecf20Sopenharmony_ci if (user_reset) 1278c2ecf20Sopenharmony_ci can_init_stats(net); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* restart counting on jiffies overflow */ 1308c2ecf20Sopenharmony_ci if (j < pkg_stats->jiffies_init) 1318c2ecf20Sopenharmony_ci can_init_stats(net); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* prevent overflow in calc_rate() */ 1348c2ecf20Sopenharmony_ci if (pkg_stats->rx_frames > (ULONG_MAX / HZ)) 1358c2ecf20Sopenharmony_ci can_init_stats(net); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* prevent overflow in calc_rate() */ 1388c2ecf20Sopenharmony_ci if (pkg_stats->tx_frames > (ULONG_MAX / HZ)) 1398c2ecf20Sopenharmony_ci can_init_stats(net); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* matches overflow - very improbable */ 1428c2ecf20Sopenharmony_ci if (pkg_stats->matches > (ULONG_MAX / 100)) 1438c2ecf20Sopenharmony_ci can_init_stats(net); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* calc total values */ 1468c2ecf20Sopenharmony_ci if (pkg_stats->rx_frames) 1478c2ecf20Sopenharmony_ci pkg_stats->total_rx_match_ratio = (pkg_stats->matches * 100) / 1488c2ecf20Sopenharmony_ci pkg_stats->rx_frames; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci pkg_stats->total_tx_rate = calc_rate(pkg_stats->jiffies_init, j, 1518c2ecf20Sopenharmony_ci pkg_stats->tx_frames); 1528c2ecf20Sopenharmony_ci pkg_stats->total_rx_rate = calc_rate(pkg_stats->jiffies_init, j, 1538c2ecf20Sopenharmony_ci pkg_stats->rx_frames); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* calc current values */ 1568c2ecf20Sopenharmony_ci if (pkg_stats->rx_frames_delta) 1578c2ecf20Sopenharmony_ci pkg_stats->current_rx_match_ratio = 1588c2ecf20Sopenharmony_ci (pkg_stats->matches_delta * 100) / 1598c2ecf20Sopenharmony_ci pkg_stats->rx_frames_delta; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci pkg_stats->current_tx_rate = calc_rate(0, HZ, pkg_stats->tx_frames_delta); 1628c2ecf20Sopenharmony_ci pkg_stats->current_rx_rate = calc_rate(0, HZ, pkg_stats->rx_frames_delta); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* check / update maximum values */ 1658c2ecf20Sopenharmony_ci if (pkg_stats->max_tx_rate < pkg_stats->current_tx_rate) 1668c2ecf20Sopenharmony_ci pkg_stats->max_tx_rate = pkg_stats->current_tx_rate; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (pkg_stats->max_rx_rate < pkg_stats->current_rx_rate) 1698c2ecf20Sopenharmony_ci pkg_stats->max_rx_rate = pkg_stats->current_rx_rate; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (pkg_stats->max_rx_match_ratio < pkg_stats->current_rx_match_ratio) 1728c2ecf20Sopenharmony_ci pkg_stats->max_rx_match_ratio = pkg_stats->current_rx_match_ratio; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* clear values for 'current rate' calculation */ 1758c2ecf20Sopenharmony_ci pkg_stats->tx_frames_delta = 0; 1768c2ecf20Sopenharmony_ci pkg_stats->rx_frames_delta = 0; 1778c2ecf20Sopenharmony_ci pkg_stats->matches_delta = 0; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* restart timer (one second) */ 1808c2ecf20Sopenharmony_ci mod_timer(&net->can.stattimer, round_jiffies(jiffies + HZ)); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* 1848c2ecf20Sopenharmony_ci * proc read functions 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list, 1888c2ecf20Sopenharmony_ci struct net_device *dev) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct receiver *r; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(r, rx_list, list) { 1938c2ecf20Sopenharmony_ci char *fmt = (r->can_id & CAN_EFF_FLAG)? 1948c2ecf20Sopenharmony_ci " %-5s %08x %08x %pK %pK %8ld %s\n" : 1958c2ecf20Sopenharmony_ci " %-5s %03x %08x %pK %pK %8ld %s\n"; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask, 1988c2ecf20Sopenharmony_ci r->func, r->data, r->matches, r->ident); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic void can_print_recv_banner(struct seq_file *m) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * can1. 00000000 00000000 00000000 2068c2ecf20Sopenharmony_ci * ....... 0 tp20 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci seq_puts(m, " device can_id can_mask function" 2098c2ecf20Sopenharmony_ci " userdata matches ident\n"); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int can_stats_proc_show(struct seq_file *m, void *v) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct net *net = m->private; 2158c2ecf20Sopenharmony_ci struct can_pkg_stats *pkg_stats = net->can.pkg_stats; 2168c2ecf20Sopenharmony_ci struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 2198c2ecf20Sopenharmony_ci seq_printf(m, " %8ld transmitted frames (TXF)\n", pkg_stats->tx_frames); 2208c2ecf20Sopenharmony_ci seq_printf(m, " %8ld received frames (RXF)\n", pkg_stats->rx_frames); 2218c2ecf20Sopenharmony_ci seq_printf(m, " %8ld matched frames (RXMF)\n", pkg_stats->matches); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (net->can.stattimer.function == can_stat_update) { 2268c2ecf20Sopenharmony_ci seq_printf(m, " %8ld %% total match ratio (RXMR)\n", 2278c2ecf20Sopenharmony_ci pkg_stats->total_rx_match_ratio); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci seq_printf(m, " %8ld frames/s total tx rate (TXR)\n", 2308c2ecf20Sopenharmony_ci pkg_stats->total_tx_rate); 2318c2ecf20Sopenharmony_ci seq_printf(m, " %8ld frames/s total rx rate (RXR)\n", 2328c2ecf20Sopenharmony_ci pkg_stats->total_rx_rate); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci seq_printf(m, " %8ld %% current match ratio (CRXMR)\n", 2378c2ecf20Sopenharmony_ci pkg_stats->current_rx_match_ratio); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci seq_printf(m, " %8ld frames/s current tx rate (CTXR)\n", 2408c2ecf20Sopenharmony_ci pkg_stats->current_tx_rate); 2418c2ecf20Sopenharmony_ci seq_printf(m, " %8ld frames/s current rx rate (CRXR)\n", 2428c2ecf20Sopenharmony_ci pkg_stats->current_rx_rate); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci seq_printf(m, " %8ld %% max match ratio (MRXMR)\n", 2478c2ecf20Sopenharmony_ci pkg_stats->max_rx_match_ratio); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci seq_printf(m, " %8ld frames/s max tx rate (MTXR)\n", 2508c2ecf20Sopenharmony_ci pkg_stats->max_tx_rate); 2518c2ecf20Sopenharmony_ci seq_printf(m, " %8ld frames/s max rx rate (MRXR)\n", 2528c2ecf20Sopenharmony_ci pkg_stats->max_rx_rate); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci seq_printf(m, " %8ld current receive list entries (CRCV)\n", 2588c2ecf20Sopenharmony_ci rcv_lists_stats->rcv_entries); 2598c2ecf20Sopenharmony_ci seq_printf(m, " %8ld maximum receive list entries (MRCV)\n", 2608c2ecf20Sopenharmony_ci rcv_lists_stats->rcv_entries_max); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (rcv_lists_stats->stats_reset) 2638c2ecf20Sopenharmony_ci seq_printf(m, "\n %8ld statistic resets (STR)\n", 2648c2ecf20Sopenharmony_ci rcv_lists_stats->stats_reset); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (rcv_lists_stats->user_reset) 2678c2ecf20Sopenharmony_ci seq_printf(m, " %8ld user statistic resets (USTR)\n", 2688c2ecf20Sopenharmony_ci rcv_lists_stats->user_reset); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int can_reset_stats_proc_show(struct seq_file *m, void *v) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct net *net = m->private; 2778c2ecf20Sopenharmony_ci struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats; 2788c2ecf20Sopenharmony_ci struct can_pkg_stats *pkg_stats = net->can.pkg_stats; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci user_reset = 1; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (net->can.stattimer.function == can_stat_update) { 2838c2ecf20Sopenharmony_ci seq_printf(m, "Scheduled statistic reset #%ld.\n", 2848c2ecf20Sopenharmony_ci rcv_lists_stats->stats_reset + 1); 2858c2ecf20Sopenharmony_ci } else { 2868c2ecf20Sopenharmony_ci if (pkg_stats->jiffies_init != jiffies) 2878c2ecf20Sopenharmony_ci can_init_stats(net); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci seq_printf(m, "Performed statistic reset #%ld.\n", 2908c2ecf20Sopenharmony_ci rcv_lists_stats->stats_reset); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic inline void can_rcvlist_proc_show_one(struct seq_file *m, int idx, 2968c2ecf20Sopenharmony_ci struct net_device *dev, 2978c2ecf20Sopenharmony_ci struct can_dev_rcv_lists *dev_rcv_lists) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci if (!hlist_empty(&dev_rcv_lists->rx[idx])) { 3008c2ecf20Sopenharmony_ci can_print_recv_banner(m); 3018c2ecf20Sopenharmony_ci can_print_rcvlist(m, &dev_rcv_lists->rx[idx], dev); 3028c2ecf20Sopenharmony_ci } else 3038c2ecf20Sopenharmony_ci seq_printf(m, " (%s: no entry)\n", DNAME(dev)); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int can_rcvlist_proc_show(struct seq_file *m, void *v) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci /* double cast to prevent GCC warning */ 3108c2ecf20Sopenharmony_ci int idx = (int)(long)PDE_DATA(m->file->f_inode); 3118c2ecf20Sopenharmony_ci struct net_device *dev; 3128c2ecf20Sopenharmony_ci struct can_dev_rcv_lists *dev_rcv_lists; 3138c2ecf20Sopenharmony_ci struct net *net = m->private; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci seq_printf(m, "\nreceive list '%s':\n", rx_list_name[idx]); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci rcu_read_lock(); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* receive list for 'all' CAN devices (dev == NULL) */ 3208c2ecf20Sopenharmony_ci dev_rcv_lists = net->can.rx_alldev_list; 3218c2ecf20Sopenharmony_ci can_rcvlist_proc_show_one(m, idx, NULL, dev_rcv_lists); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* receive list for registered CAN devices */ 3248c2ecf20Sopenharmony_ci for_each_netdev_rcu(net, dev) { 3258c2ecf20Sopenharmony_ci struct can_ml_priv *can_ml = can_get_ml_priv(dev); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (can_ml) 3288c2ecf20Sopenharmony_ci can_rcvlist_proc_show_one(m, idx, dev, 3298c2ecf20Sopenharmony_ci &can_ml->dev_rcv_lists); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci rcu_read_unlock(); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic inline void can_rcvlist_proc_show_array(struct seq_file *m, 3398c2ecf20Sopenharmony_ci struct net_device *dev, 3408c2ecf20Sopenharmony_ci struct hlist_head *rcv_array, 3418c2ecf20Sopenharmony_ci unsigned int rcv_array_sz) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci unsigned int i; 3448c2ecf20Sopenharmony_ci int all_empty = 1; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* check whether at least one list is non-empty */ 3478c2ecf20Sopenharmony_ci for (i = 0; i < rcv_array_sz; i++) 3488c2ecf20Sopenharmony_ci if (!hlist_empty(&rcv_array[i])) { 3498c2ecf20Sopenharmony_ci all_empty = 0; 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (!all_empty) { 3548c2ecf20Sopenharmony_ci can_print_recv_banner(m); 3558c2ecf20Sopenharmony_ci for (i = 0; i < rcv_array_sz; i++) { 3568c2ecf20Sopenharmony_ci if (!hlist_empty(&rcv_array[i])) 3578c2ecf20Sopenharmony_ci can_print_rcvlist(m, &rcv_array[i], dev); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci } else 3608c2ecf20Sopenharmony_ci seq_printf(m, " (%s: no entry)\n", DNAME(dev)); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int can_rcvlist_sff_proc_show(struct seq_file *m, void *v) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct net_device *dev; 3668c2ecf20Sopenharmony_ci struct can_dev_rcv_lists *dev_rcv_lists; 3678c2ecf20Sopenharmony_ci struct net *net = m->private; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* RX_SFF */ 3708c2ecf20Sopenharmony_ci seq_puts(m, "\nreceive list 'rx_sff':\n"); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci rcu_read_lock(); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* sff receive list for 'all' CAN devices (dev == NULL) */ 3758c2ecf20Sopenharmony_ci dev_rcv_lists = net->can.rx_alldev_list; 3768c2ecf20Sopenharmony_ci can_rcvlist_proc_show_array(m, NULL, dev_rcv_lists->rx_sff, 3778c2ecf20Sopenharmony_ci ARRAY_SIZE(dev_rcv_lists->rx_sff)); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* sff receive list for registered CAN devices */ 3808c2ecf20Sopenharmony_ci for_each_netdev_rcu(net, dev) { 3818c2ecf20Sopenharmony_ci struct can_ml_priv *can_ml = can_get_ml_priv(dev); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (can_ml) { 3848c2ecf20Sopenharmony_ci dev_rcv_lists = &can_ml->dev_rcv_lists; 3858c2ecf20Sopenharmony_ci can_rcvlist_proc_show_array(m, dev, dev_rcv_lists->rx_sff, 3868c2ecf20Sopenharmony_ci ARRAY_SIZE(dev_rcv_lists->rx_sff)); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci rcu_read_unlock(); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int can_rcvlist_eff_proc_show(struct seq_file *m, void *v) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct net_device *dev; 3998c2ecf20Sopenharmony_ci struct can_dev_rcv_lists *dev_rcv_lists; 4008c2ecf20Sopenharmony_ci struct net *net = m->private; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* RX_EFF */ 4038c2ecf20Sopenharmony_ci seq_puts(m, "\nreceive list 'rx_eff':\n"); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci rcu_read_lock(); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* eff receive list for 'all' CAN devices (dev == NULL) */ 4088c2ecf20Sopenharmony_ci dev_rcv_lists = net->can.rx_alldev_list; 4098c2ecf20Sopenharmony_ci can_rcvlist_proc_show_array(m, NULL, dev_rcv_lists->rx_eff, 4108c2ecf20Sopenharmony_ci ARRAY_SIZE(dev_rcv_lists->rx_eff)); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* eff receive list for registered CAN devices */ 4138c2ecf20Sopenharmony_ci for_each_netdev_rcu(net, dev) { 4148c2ecf20Sopenharmony_ci struct can_ml_priv *can_ml = can_get_ml_priv(dev); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (can_ml) { 4178c2ecf20Sopenharmony_ci dev_rcv_lists = &can_ml->dev_rcv_lists; 4188c2ecf20Sopenharmony_ci can_rcvlist_proc_show_array(m, dev, dev_rcv_lists->rx_eff, 4198c2ecf20Sopenharmony_ci ARRAY_SIZE(dev_rcv_lists->rx_eff)); 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci rcu_read_unlock(); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 4268c2ecf20Sopenharmony_ci return 0; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci/* 4308c2ecf20Sopenharmony_ci * can_init_proc - create main CAN proc directory and procfs entries 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_civoid can_init_proc(struct net *net) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci /* create /proc/net/can directory */ 4358c2ecf20Sopenharmony_ci net->can.proc_dir = proc_net_mkdir(net, "can", net->proc_net); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (!net->can.proc_dir) { 4388c2ecf20Sopenharmony_ci printk(KERN_INFO "can: failed to create /proc/net/can . " 4398c2ecf20Sopenharmony_ci "CONFIG_PROC_FS missing?\n"); 4408c2ecf20Sopenharmony_ci return; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* own procfs entries from the AF_CAN core */ 4448c2ecf20Sopenharmony_ci net->can.pde_stats = proc_create_net_single(CAN_PROC_STATS, 0644, 4458c2ecf20Sopenharmony_ci net->can.proc_dir, can_stats_proc_show, NULL); 4468c2ecf20Sopenharmony_ci net->can.pde_reset_stats = proc_create_net_single(CAN_PROC_RESET_STATS, 4478c2ecf20Sopenharmony_ci 0644, net->can.proc_dir, can_reset_stats_proc_show, 4488c2ecf20Sopenharmony_ci NULL); 4498c2ecf20Sopenharmony_ci net->can.pde_rcvlist_err = proc_create_net_single(CAN_PROC_RCVLIST_ERR, 4508c2ecf20Sopenharmony_ci 0644, net->can.proc_dir, can_rcvlist_proc_show, 4518c2ecf20Sopenharmony_ci (void *)RX_ERR); 4528c2ecf20Sopenharmony_ci net->can.pde_rcvlist_all = proc_create_net_single(CAN_PROC_RCVLIST_ALL, 4538c2ecf20Sopenharmony_ci 0644, net->can.proc_dir, can_rcvlist_proc_show, 4548c2ecf20Sopenharmony_ci (void *)RX_ALL); 4558c2ecf20Sopenharmony_ci net->can.pde_rcvlist_fil = proc_create_net_single(CAN_PROC_RCVLIST_FIL, 4568c2ecf20Sopenharmony_ci 0644, net->can.proc_dir, can_rcvlist_proc_show, 4578c2ecf20Sopenharmony_ci (void *)RX_FIL); 4588c2ecf20Sopenharmony_ci net->can.pde_rcvlist_inv = proc_create_net_single(CAN_PROC_RCVLIST_INV, 4598c2ecf20Sopenharmony_ci 0644, net->can.proc_dir, can_rcvlist_proc_show, 4608c2ecf20Sopenharmony_ci (void *)RX_INV); 4618c2ecf20Sopenharmony_ci net->can.pde_rcvlist_eff = proc_create_net_single(CAN_PROC_RCVLIST_EFF, 4628c2ecf20Sopenharmony_ci 0644, net->can.proc_dir, can_rcvlist_eff_proc_show, NULL); 4638c2ecf20Sopenharmony_ci net->can.pde_rcvlist_sff = proc_create_net_single(CAN_PROC_RCVLIST_SFF, 4648c2ecf20Sopenharmony_ci 0644, net->can.proc_dir, can_rcvlist_sff_proc_show, NULL); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/* 4688c2ecf20Sopenharmony_ci * can_remove_proc - remove procfs entries and main CAN proc directory 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_civoid can_remove_proc(struct net *net) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci if (!net->can.proc_dir) 4738c2ecf20Sopenharmony_ci return; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (net->can.pde_stats) 4768c2ecf20Sopenharmony_ci remove_proc_entry(CAN_PROC_STATS, net->can.proc_dir); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (net->can.pde_reset_stats) 4798c2ecf20Sopenharmony_ci remove_proc_entry(CAN_PROC_RESET_STATS, net->can.proc_dir); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (net->can.pde_rcvlist_err) 4828c2ecf20Sopenharmony_ci remove_proc_entry(CAN_PROC_RCVLIST_ERR, net->can.proc_dir); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (net->can.pde_rcvlist_all) 4858c2ecf20Sopenharmony_ci remove_proc_entry(CAN_PROC_RCVLIST_ALL, net->can.proc_dir); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (net->can.pde_rcvlist_fil) 4888c2ecf20Sopenharmony_ci remove_proc_entry(CAN_PROC_RCVLIST_FIL, net->can.proc_dir); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (net->can.pde_rcvlist_inv) 4918c2ecf20Sopenharmony_ci remove_proc_entry(CAN_PROC_RCVLIST_INV, net->can.proc_dir); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (net->can.pde_rcvlist_eff) 4948c2ecf20Sopenharmony_ci remove_proc_entry(CAN_PROC_RCVLIST_EFF, net->can.proc_dir); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (net->can.pde_rcvlist_sff) 4978c2ecf20Sopenharmony_ci remove_proc_entry(CAN_PROC_RCVLIST_SFF, net->can.proc_dir); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci remove_proc_entry("can", net->proc_net); 5008c2ecf20Sopenharmony_ci} 501