aboutsummaryrefslogtreecommitdiffstats
path: root/net/can/proc.c
diff options
context:
space:
mode:
authorOliver Hartkopp <oliver.hartkopp@volkswagen.de>2007-11-16 18:52:17 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 17:54:10 -0500
commit0d66548a10cbbe0ef256852d63d30603f0f73f9b (patch)
tree58c91033f88014c6a25b4d105a721bc58ec35f2c /net/can/proc.c
parentcd05acfe65ed2cf2db683fa9a6adb8d35635263b (diff)
[CAN]: Add PF_CAN core module
This patch adds the CAN core functionality but no protocols or drivers. No protocol implementations are included here. They come as separate patches. Protocol numbers are already in include/linux/can.h. Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de> Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/can/proc.c')
-rw-r--r--net/can/proc.c533
1 files changed, 533 insertions, 0 deletions
diff --git a/net/can/proc.c b/net/can/proc.c
new file mode 100644
index 000000000000..520fef5e5398
--- /dev/null
+++ b/net/can/proc.c
@@ -0,0 +1,533 @@
1/*
2 * proc.c - procfs support for Protocol family CAN core module
3 *
4 * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Volkswagen nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * Alternatively, provided that this notice is retained in full, this
20 * software may be distributed under the terms of the GNU General
21 * Public License ("GPL") version 2, in which case the provisions of the
22 * GPL apply INSTEAD OF those given above.
23 *
24 * The provided data structures and external interfaces from this code
25 * are not restricted to be used by modules with a GPL compatible license.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
38 * DAMAGE.
39 *
40 * Send feedback to <socketcan-users@lists.berlios.de>
41 *
42 */
43
44#include <linux/module.h>
45#include <linux/proc_fs.h>
46#include <linux/list.h>
47#include <linux/rcupdate.h>
48#include <linux/can/core.h>
49
50#include "af_can.h"
51
52/*
53 * proc filenames for the PF_CAN core
54 */
55
56#define CAN_PROC_VERSION "version"
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
66static struct proc_dir_entry *can_dir;
67static struct proc_dir_entry *pde_version;
68static struct proc_dir_entry *pde_stats;
69static struct proc_dir_entry *pde_reset_stats;
70static struct proc_dir_entry *pde_rcvlist_all;
71static struct proc_dir_entry *pde_rcvlist_fil;
72static struct proc_dir_entry *pde_rcvlist_inv;
73static struct proc_dir_entry *pde_rcvlist_sff;
74static struct proc_dir_entry *pde_rcvlist_eff;
75static struct proc_dir_entry *pde_rcvlist_err;
76
77static int user_reset;
78
79static const char rx_list_name[][8] = {
80 [RX_ERR] = "rx_err",
81 [RX_ALL] = "rx_all",
82 [RX_FIL] = "rx_fil",
83 [RX_INV] = "rx_inv",
84 [RX_EFF] = "rx_eff",
85};
86
87/*
88 * af_can statistics stuff
89 */
90
91static void can_init_stats(void)
92{
93 /*
94 * This memset function is called from a timer context (when
95 * can_stattimer is active which is the default) OR in a process
96 * context (reading the proc_fs when can_stattimer is disabled).
97 */
98 memset(&can_stats, 0, sizeof(can_stats));
99 can_stats.jiffies_init = jiffies;
100
101 can_pstats.stats_reset++;
102
103 if (user_reset) {
104 user_reset = 0;
105 can_pstats.user_reset++;
106 }
107}
108
109static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
110 unsigned long count)
111{
112 unsigned long rate;
113
114 if (oldjif == newjif)
115 return 0;
116
117 /* see can_stat_update() - this should NEVER happen! */
118 if (count > (ULONG_MAX / HZ)) {
119 printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
120 count);
121 return 99999999;
122 }
123
124 rate = (count * HZ) / (newjif - oldjif);
125
126 return rate;
127}
128
129void can_stat_update(unsigned long data)
130{
131 unsigned long j = jiffies; /* snapshot */
132
133 /* restart counting in timer context on user request */
134 if (user_reset)
135 can_init_stats();
136
137 /* restart counting on jiffies overflow */
138 if (j < can_stats.jiffies_init)
139 can_init_stats();
140
141 /* prevent overflow in calc_rate() */
142 if (can_stats.rx_frames > (ULONG_MAX / HZ))
143 can_init_stats();
144
145 /* prevent overflow in calc_rate() */
146 if (can_stats.tx_frames > (ULONG_MAX / HZ))
147 can_init_stats();
148
149 /* matches overflow - very improbable */
150 if (can_stats.matches > (ULONG_MAX / 100))
151 can_init_stats();
152
153 /* calc total values */
154 if (can_stats.rx_frames)
155 can_stats.total_rx_match_ratio = (can_stats.matches * 100) /
156 can_stats.rx_frames;
157
158 can_stats.total_tx_rate = calc_rate(can_stats.jiffies_init, j,
159 can_stats.tx_frames);
160 can_stats.total_rx_rate = calc_rate(can_stats.jiffies_init, j,
161 can_stats.rx_frames);
162
163 /* calc current values */
164 if (can_stats.rx_frames_delta)
165 can_stats.current_rx_match_ratio =
166 (can_stats.matches_delta * 100) /
167 can_stats.rx_frames_delta;
168
169 can_stats.current_tx_rate = calc_rate(0, HZ, can_stats.tx_frames_delta);
170 can_stats.current_rx_rate = calc_rate(0, HZ, can_stats.rx_frames_delta);
171
172 /* check / update maximum values */
173 if (can_stats.max_tx_rate < can_stats.current_tx_rate)
174 can_stats.max_tx_rate = can_stats.current_tx_rate;
175
176 if (can_stats.max_rx_rate < can_stats.current_rx_rate)
177 can_stats.max_rx_rate = can_stats.current_rx_rate;
178
179 if (can_stats.max_rx_match_ratio < can_stats.current_rx_match_ratio)
180 can_stats.max_rx_match_ratio = can_stats.current_rx_match_ratio;
181
182 /* clear values for 'current rate' calculation */
183 can_stats.tx_frames_delta = 0;
184 can_stats.rx_frames_delta = 0;
185 can_stats.matches_delta = 0;
186
187 /* restart timer (one second) */
188 mod_timer(&can_stattimer, round_jiffies(jiffies + HZ));
189}
190
191/*
192 * proc read functions
193 *
194 * From known use-cases we expect about 10 entries in a receive list to be
195 * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
196 *
197 */
198
199static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
200 struct net_device *dev)
201{
202 struct receiver *r;
203 struct hlist_node *n;
204
205 rcu_read_lock();
206 hlist_for_each_entry_rcu(r, n, rx_list, list) {
207 char *fmt = (r->can_id & CAN_EFF_FLAG)?
208 " %-5s %08X %08x %08x %08x %8ld %s\n" :
209 " %-5s %03X %08x %08lx %08lx %8ld %s\n";
210
211 len += snprintf(page + len, PAGE_SIZE - len, fmt,
212 DNAME(dev), r->can_id, r->mask,
213 (unsigned long)r->func, (unsigned long)r->data,
214 r->matches, r->ident);
215
216 /* does a typical line fit into the current buffer? */
217
218 /* 100 Bytes before end of buffer */
219 if (len > PAGE_SIZE - 100) {
220 /* mark output cut off */
221 len += snprintf(page + len, PAGE_SIZE - len,
222 " (..)\n");
223 break;
224 }
225 }
226 rcu_read_unlock();
227
228 return len;
229}
230
231static int can_print_recv_banner(char *page, int len)
232{
233 /*
234 * can1. 00000000 00000000 00000000
235 * ....... 0 tp20
236 */
237 len += snprintf(page + len, PAGE_SIZE - len,
238 " device can_id can_mask function"
239 " userdata matches ident\n");
240
241 return len;
242}
243
244static int can_proc_read_stats(char *page, char **start, off_t off,
245 int count, int *eof, void *data)
246{
247 int len = 0;
248
249 len += snprintf(page + len, PAGE_SIZE - len, "\n");
250 len += snprintf(page + len, PAGE_SIZE - len,
251 " %8ld transmitted frames (TXF)\n",
252 can_stats.tx_frames);
253 len += snprintf(page + len, PAGE_SIZE - len,
254 " %8ld received frames (RXF)\n", can_stats.rx_frames);
255 len += snprintf(page + len, PAGE_SIZE - len,
256 " %8ld matched frames (RXMF)\n", can_stats.matches);
257
258 len += snprintf(page + len, PAGE_SIZE - len, "\n");
259
260 if (can_stattimer.function == can_stat_update) {
261 len += snprintf(page + len, PAGE_SIZE - len,
262 " %8ld %% total match ratio (RXMR)\n",
263 can_stats.total_rx_match_ratio);
264
265 len += snprintf(page + len, PAGE_SIZE - len,
266 " %8ld frames/s total tx rate (TXR)\n",
267 can_stats.total_tx_rate);
268 len += snprintf(page + len, PAGE_SIZE - len,
269 " %8ld frames/s total rx rate (RXR)\n",
270 can_stats.total_rx_rate);
271
272 len += snprintf(page + len, PAGE_SIZE - len, "\n");
273
274 len += snprintf(page + len, PAGE_SIZE - len,
275 " %8ld %% current match ratio (CRXMR)\n",
276 can_stats.current_rx_match_ratio);
277
278 len += snprintf(page + len, PAGE_SIZE - len,
279 " %8ld frames/s current tx rate (CTXR)\n",
280 can_stats.current_tx_rate);
281 len += snprintf(page + len, PAGE_SIZE - len,
282 " %8ld frames/s current rx rate (CRXR)\n",
283 can_stats.current_rx_rate);
284
285 len += snprintf(page + len, PAGE_SIZE - len, "\n");
286
287 len += snprintf(page + len, PAGE_SIZE - len,
288 " %8ld %% max match ratio (MRXMR)\n",
289 can_stats.max_rx_match_ratio);
290
291 len += snprintf(page + len, PAGE_SIZE - len,
292 " %8ld frames/s max tx rate (MTXR)\n",
293 can_stats.max_tx_rate);
294 len += snprintf(page + len, PAGE_SIZE - len,
295 " %8ld frames/s max rx rate (MRXR)\n",
296 can_stats.max_rx_rate);
297
298 len += snprintf(page + len, PAGE_SIZE - len, "\n");
299 }
300
301 len += snprintf(page + len, PAGE_SIZE - len,
302 " %8ld current receive list entries (CRCV)\n",
303 can_pstats.rcv_entries);
304 len += snprintf(page + len, PAGE_SIZE - len,
305 " %8ld maximum receive list entries (MRCV)\n",
306 can_pstats.rcv_entries_max);
307
308 if (can_pstats.stats_reset)
309 len += snprintf(page + len, PAGE_SIZE - len,
310 "\n %8ld statistic resets (STR)\n",
311 can_pstats.stats_reset);
312
313 if (can_pstats.user_reset)
314 len += snprintf(page + len, PAGE_SIZE - len,
315 " %8ld user statistic resets (USTR)\n",
316 can_pstats.user_reset);
317
318 len += snprintf(page + len, PAGE_SIZE - len, "\n");
319
320 *eof = 1;
321 return len;
322}
323
324static int can_proc_read_reset_stats(char *page, char **start, off_t off,
325 int count, int *eof, void *data)
326{
327 int len = 0;
328
329 user_reset = 1;
330
331 if (can_stattimer.function == can_stat_update) {
332 len += snprintf(page + len, PAGE_SIZE - len,
333 "Scheduled statistic reset #%ld.\n",
334 can_pstats.stats_reset + 1);
335
336 } else {
337 if (can_stats.jiffies_init != jiffies)
338 can_init_stats();
339
340 len += snprintf(page + len, PAGE_SIZE - len,
341 "Performed statistic reset #%ld.\n",
342 can_pstats.stats_reset);
343 }
344
345 *eof = 1;
346 return len;
347}
348
349static int can_proc_read_version(char *page, char **start, off_t off,
350 int count, int *eof, void *data)
351{
352 int len = 0;
353
354 len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
355 CAN_VERSION_STRING);
356 *eof = 1;
357 return len;
358}
359
360static int can_proc_read_rcvlist(char *page, char **start, off_t off,
361 int count, int *eof, void *data)
362{
363 /* double cast to prevent GCC warning */
364 int idx = (int)(long)data;
365 int len = 0;
366 struct dev_rcv_lists *d;
367 struct hlist_node *n;
368
369 len += snprintf(page + len, PAGE_SIZE - len,
370 "\nreceive list '%s':\n", rx_list_name[idx]);
371
372 rcu_read_lock();
373 hlist_for_each_entry_rcu(d, n, &can_rx_dev_list, list) {
374
375 if (!hlist_empty(&d->rx[idx])) {
376 len = can_print_recv_banner(page, len);
377 len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
378 } else
379 len += snprintf(page + len, PAGE_SIZE - len,
380 " (%s: no entry)\n", DNAME(d->dev));
381
382 /* exit on end of buffer? */
383 if (len > PAGE_SIZE - 100)
384 break;
385 }
386 rcu_read_unlock();
387
388 len += snprintf(page + len, PAGE_SIZE - len, "\n");
389
390 *eof = 1;
391 return len;
392}
393
394static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
395 int count, int *eof, void *data)
396{
397 int len = 0;
398 struct dev_rcv_lists *d;
399 struct hlist_node *n;
400
401 /* RX_SFF */
402 len += snprintf(page + len, PAGE_SIZE - len,
403 "\nreceive list 'rx_sff':\n");
404
405 rcu_read_lock();
406 hlist_for_each_entry_rcu(d, n, &can_rx_dev_list, list) {
407 int i, all_empty = 1;
408 /* check wether at least one list is non-empty */
409 for (i = 0; i < 0x800; i++)
410 if (!hlist_empty(&d->rx_sff[i])) {
411 all_empty = 0;
412 break;
413 }
414
415 if (!all_empty) {
416 len = can_print_recv_banner(page, len);
417 for (i = 0; i < 0x800; i++) {
418 if (!hlist_empty(&d->rx_sff[i]) &&
419 len < PAGE_SIZE - 100)
420 len = can_print_rcvlist(page, len,
421 &d->rx_sff[i],
422 d->dev);
423 }
424 } else
425 len += snprintf(page + len, PAGE_SIZE - len,
426 " (%s: no entry)\n", DNAME(d->dev));
427
428 /* exit on end of buffer? */
429 if (len > PAGE_SIZE - 100)
430 break;
431 }
432 rcu_read_unlock();
433
434 len += snprintf(page + len, PAGE_SIZE - len, "\n");
435
436 *eof = 1;
437 return len;
438}
439
440/*
441 * proc utility functions
442 */
443
444static struct proc_dir_entry *can_create_proc_readentry(const char *name,
445 mode_t mode,
446 read_proc_t *read_proc,
447 void *data)
448{
449 if (can_dir)
450 return create_proc_read_entry(name, mode, can_dir, read_proc,
451 data);
452 else
453 return NULL;
454}
455
456static void can_remove_proc_readentry(const char *name)
457{
458 if (can_dir)
459 remove_proc_entry(name, can_dir);
460}
461
462/*
463 * can_init_proc - create main CAN proc directory and procfs entries
464 */
465void can_init_proc(void)
466{
467 /* create /proc/net/can directory */
468 can_dir = proc_mkdir("can", init_net.proc_net);
469
470 if (!can_dir) {
471 printk(KERN_INFO "can: failed to create /proc/net/can . "
472 "CONFIG_PROC_FS missing?\n");
473 return;
474 }
475
476 can_dir->owner = THIS_MODULE;
477
478 /* own procfs entries from the AF_CAN core */
479 pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
480 can_proc_read_version, NULL);
481 pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
482 can_proc_read_stats, NULL);
483 pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
484 can_proc_read_reset_stats, NULL);
485 pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
486 can_proc_read_rcvlist, (void *)RX_ERR);
487 pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
488 can_proc_read_rcvlist, (void *)RX_ALL);
489 pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
490 can_proc_read_rcvlist, (void *)RX_FIL);
491 pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
492 can_proc_read_rcvlist, (void *)RX_INV);
493 pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
494 can_proc_read_rcvlist, (void *)RX_EFF);
495 pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
496 can_proc_read_rcvlist_sff, NULL);
497}
498
499/*
500 * can_remove_proc - remove procfs entries and main CAN proc directory
501 */
502void can_remove_proc(void)
503{
504 if (pde_version)
505 can_remove_proc_readentry(CAN_PROC_VERSION);
506
507 if (pde_stats)
508 can_remove_proc_readentry(CAN_PROC_STATS);
509
510 if (pde_reset_stats)
511 can_remove_proc_readentry(CAN_PROC_RESET_STATS);
512
513 if (pde_rcvlist_err)
514 can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR);
515
516 if (pde_rcvlist_all)
517 can_remove_proc_readentry(CAN_PROC_RCVLIST_ALL);
518
519 if (pde_rcvlist_fil)
520 can_remove_proc_readentry(CAN_PROC_RCVLIST_FIL);
521
522 if (pde_rcvlist_inv)
523 can_remove_proc_readentry(CAN_PROC_RCVLIST_INV);
524
525 if (pde_rcvlist_eff)
526 can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF);
527
528 if (pde_rcvlist_sff)
529 can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF);
530
531 if (can_dir)
532 proc_net_remove(&init_net, "can");
533}