| /* | |
| * | |
| * linux/drivers/s390/net/qeth_fs.c | |
| * | |
| * Linux on zSeries OSA Express and HiperSockets support | |
| * This file contains code related to procfs. | |
| * | |
| * Copyright 2000,2003 IBM Corporation | |
| * | |
| * Author(s): Thomas Spatzier <tspat@de.ibm.com> | |
| * | |
| */ | |
| #include <linux/module.h> | |
| #include <linux/init.h> | |
| #include <linux/proc_fs.h> | |
| #include <linux/seq_file.h> | |
| #include <linux/list.h> | |
| #include <linux/rwsem.h> | |
| #include "qeth.h" | |
| #include "qeth_mpc.h" | |
| #include "qeth_fs.h" | |
| /***** /proc/qeth *****/ | |
| #define QETH_PROCFILE_NAME "qeth" | |
| static struct proc_dir_entry *qeth_procfile; | |
| static int | |
| qeth_procfile_seq_match(struct device *dev, void *data) | |
| { | |
| return(dev ? 1 : 0); | |
| } | |
| static void * | |
| qeth_procfile_seq_start(struct seq_file *s, loff_t *offset) | |
| { | |
| struct device *dev = NULL; | |
| loff_t nr = 0; | |
| down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); | |
| if (*offset == 0) | |
| return SEQ_START_TOKEN; | |
| while (1) { | |
| dev = driver_find_device(&qeth_ccwgroup_driver.driver, dev, | |
| NULL, qeth_procfile_seq_match); | |
| if (++nr == *offset) | |
| break; | |
| put_device(dev); | |
| } | |
| return dev; | |
| } | |
| static void | |
| qeth_procfile_seq_stop(struct seq_file *s, void* it) | |
| { | |
| up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); | |
| } | |
| static void * | |
| qeth_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset) | |
| { | |
| struct device *prev, *next; | |
| if (it == SEQ_START_TOKEN) | |
| prev = NULL; | |
| else | |
| prev = (struct device *) it; | |
| next = driver_find_device(&qeth_ccwgroup_driver.driver, | |
| prev, NULL, qeth_procfile_seq_match); | |
| (*offset)++; | |
| return (void *) next; | |
| } | |
| static inline const char * | |
| qeth_get_router_str(struct qeth_card *card, int ipv) | |
| { | |
| enum qeth_routing_types routing_type = NO_ROUTER; | |
| if (ipv == 4) { | |
| routing_type = card->options.route4.type; | |
| } else { | |
| #ifdef CONFIG_QETH_IPV6 | |
| routing_type = card->options.route6.type; | |
| #else | |
| return "n/a"; | |
| #endif /* CONFIG_QETH_IPV6 */ | |
| } | |
| switch (routing_type){ | |
| case PRIMARY_ROUTER: | |
| return "pri"; | |
| case SECONDARY_ROUTER: | |
| return "sec"; | |
| case MULTICAST_ROUTER: | |
| if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | |
| return "mc+"; | |
| return "mc"; | |
| case PRIMARY_CONNECTOR: | |
| if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | |
| return "p+c"; | |
| return "p.c"; | |
| case SECONDARY_CONNECTOR: | |
| if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | |
| return "s+c"; | |
| return "s.c"; | |
| default: /* NO_ROUTER */ | |
| return "no"; | |
| } | |
| } | |
| static int | |
| qeth_procfile_seq_show(struct seq_file *s, void *it) | |
| { | |
| struct device *device; | |
| struct qeth_card *card; | |
| char tmp[12]; /* for qeth_get_prioq_str */ | |
| if (it == SEQ_START_TOKEN){ | |
| seq_printf(s, "devices CHPID interface " | |
| "cardtype port chksum prio-q'ing rtr4 " | |
| "rtr6 fsz cnt\n"); | |
| seq_printf(s, "-------------------------- ----- ---------- " | |
| "-------------- ---- ------ ---------- ---- " | |
| "---- ----- -----\n"); | |
| } else { | |
| device = (struct device *) it; | |
| card = device->driver_data; | |
| seq_printf(s, "%s/%s/%s x%02X %-10s %-14s %-4i ", | |
| CARD_RDEV_ID(card), | |
| CARD_WDEV_ID(card), | |
| CARD_DDEV_ID(card), | |
| card->info.chpid, | |
| QETH_CARD_IFNAME(card), | |
| qeth_get_cardname_short(card), | |
| card->info.portno); | |
| if (card->lan_online) | |
| seq_printf(s, "%-6s %-10s %-4s %-4s %-5s %-5i\n", | |
| qeth_get_checksum_str(card), | |
| qeth_get_prioq_str(card, tmp), | |
| qeth_get_router_str(card, 4), | |
| qeth_get_router_str(card, 6), | |
| qeth_get_bufsize_str(card), | |
| card->qdio.in_buf_pool.buf_count); | |
| else | |
| seq_printf(s, " +++ LAN OFFLINE +++\n"); | |
| put_device(device); | |
| } | |
| return 0; | |
| } | |
| static struct seq_operations qeth_procfile_seq_ops = { | |
| .start = qeth_procfile_seq_start, | |
| .stop = qeth_procfile_seq_stop, | |
| .next = qeth_procfile_seq_next, | |
| .show = qeth_procfile_seq_show, | |
| }; | |
| static int | |
| qeth_procfile_open(struct inode *inode, struct file *file) | |
| { | |
| return seq_open(file, &qeth_procfile_seq_ops); | |
| } | |
| static struct file_operations qeth_procfile_fops = { | |
| .owner = THIS_MODULE, | |
| .open = qeth_procfile_open, | |
| .read = seq_read, | |
| .llseek = seq_lseek, | |
| .release = seq_release, | |
| }; | |
| /***** /proc/qeth_perf *****/ | |
| #define QETH_PERF_PROCFILE_NAME "qeth_perf" | |
| static struct proc_dir_entry *qeth_perf_procfile; | |
| static int | |
| qeth_perf_procfile_seq_show(struct seq_file *s, void *it) | |
| { | |
| struct device *device; | |
| struct qeth_card *card; | |
| if (it == SEQ_START_TOKEN) | |
| return 0; | |
| device = (struct device *) it; | |
| card = device->driver_data; | |
| seq_printf(s, "For card with devnos %s/%s/%s (%s):\n", | |
| CARD_RDEV_ID(card), | |
| CARD_WDEV_ID(card), | |
| CARD_DDEV_ID(card), | |
| QETH_CARD_IFNAME(card) | |
| ); | |
| if (!card->options.performance_stats) | |
| seq_printf(s, "Performance statistics are deactivated.\n"); | |
| seq_printf(s, " Skb's/buffers received : %lu/%u\n" | |
| " Skb's/buffers sent : %lu/%u\n\n", | |
| card->stats.rx_packets - | |
| card->perf_stats.initial_rx_packets, | |
| card->perf_stats.bufs_rec, | |
| card->stats.tx_packets - | |
| card->perf_stats.initial_tx_packets, | |
| card->perf_stats.bufs_sent | |
| ); | |
| seq_printf(s, " Skb's/buffers sent without packing : %lu/%u\n" | |
| " Skb's/buffers sent with packing : %u/%u\n\n", | |
| card->stats.tx_packets - card->perf_stats.initial_tx_packets | |
| - card->perf_stats.skbs_sent_pack, | |
| card->perf_stats.bufs_sent - card->perf_stats.bufs_sent_pack, | |
| card->perf_stats.skbs_sent_pack, | |
| card->perf_stats.bufs_sent_pack | |
| ); | |
| seq_printf(s, " Skbs sent in SG mode : %u\n" | |
| " Skb fragments sent in SG mode : %u\n\n", | |
| card->perf_stats.sg_skbs_sent, | |
| card->perf_stats.sg_frags_sent); | |
| seq_printf(s, " large_send tx (in Kbytes) : %u\n" | |
| " large_send count : %u\n\n", | |
| card->perf_stats.large_send_bytes >> 10, | |
| card->perf_stats.large_send_cnt); | |
| seq_printf(s, " Packing state changes no pkg.->packing : %u/%u\n" | |
| " Watermarks L/H : %i/%i\n" | |
| " Current buffer usage (outbound q's) : " | |
| "%i/%i/%i/%i\n\n", | |
| card->perf_stats.sc_dp_p, card->perf_stats.sc_p_dp, | |
| QETH_LOW_WATERMARK_PACK, QETH_HIGH_WATERMARK_PACK, | |
| atomic_read(&card->qdio.out_qs[0]->used_buffers), | |
| (card->qdio.no_out_queues > 1)? | |
| atomic_read(&card->qdio.out_qs[1]->used_buffers) | |
| : 0, | |
| (card->qdio.no_out_queues > 2)? | |
| atomic_read(&card->qdio.out_qs[2]->used_buffers) | |
| : 0, | |
| (card->qdio.no_out_queues > 3)? | |
| atomic_read(&card->qdio.out_qs[3]->used_buffers) | |
| : 0 | |
| ); | |
| seq_printf(s, " Inbound handler time (in us) : %u\n" | |
| " Inbound handler count : %u\n" | |
| " Inbound do_QDIO time (in us) : %u\n" | |
| " Inbound do_QDIO count : %u\n\n" | |
| " Outbound handler time (in us) : %u\n" | |
| " Outbound handler count : %u\n\n" | |
| " Outbound time (in us, incl QDIO) : %u\n" | |
| " Outbound count : %u\n" | |
| " Outbound do_QDIO time (in us) : %u\n" | |
| " Outbound do_QDIO count : %u\n\n", | |
| card->perf_stats.inbound_time, | |
| card->perf_stats.inbound_cnt, | |
| card->perf_stats.inbound_do_qdio_time, | |
| card->perf_stats.inbound_do_qdio_cnt, | |
| card->perf_stats.outbound_handler_time, | |
| card->perf_stats.outbound_handler_cnt, | |
| card->perf_stats.outbound_time, | |
| card->perf_stats.outbound_cnt, | |
| card->perf_stats.outbound_do_qdio_time, | |
| card->perf_stats.outbound_do_qdio_cnt | |
| ); | |
| put_device(device); | |
| return 0; | |
| } | |
| static struct seq_operations qeth_perf_procfile_seq_ops = { | |
| .start = qeth_procfile_seq_start, | |
| .stop = qeth_procfile_seq_stop, | |
| .next = qeth_procfile_seq_next, | |
| .show = qeth_perf_procfile_seq_show, | |
| }; | |
| static int | |
| qeth_perf_procfile_open(struct inode *inode, struct file *file) | |
| { | |
| return seq_open(file, &qeth_perf_procfile_seq_ops); | |
| } | |
| static struct file_operations qeth_perf_procfile_fops = { | |
| .owner = THIS_MODULE, | |
| .open = qeth_perf_procfile_open, | |
| .read = seq_read, | |
| .llseek = seq_lseek, | |
| .release = seq_release, | |
| }; | |
| int __init | |
| qeth_create_procfs_entries(void) | |
| { | |
| qeth_procfile = create_proc_entry(QETH_PROCFILE_NAME, | |
| S_IFREG | 0444, NULL); | |
| if (qeth_procfile) | |
| qeth_procfile->proc_fops = &qeth_procfile_fops; | |
| qeth_perf_procfile = create_proc_entry(QETH_PERF_PROCFILE_NAME, | |
| S_IFREG | 0444, NULL); | |
| if (qeth_perf_procfile) | |
| qeth_perf_procfile->proc_fops = &qeth_perf_procfile_fops; | |
| if (qeth_procfile && | |
| qeth_perf_procfile) | |
| return 0; | |
| else | |
| return -ENOMEM; | |
| } | |
| void __exit | |
| qeth_remove_procfs_entries(void) | |
| { | |
| if (qeth_procfile) | |
| remove_proc_entry(QETH_PROCFILE_NAME, NULL); | |
| if (qeth_perf_procfile) | |
| remove_proc_entry(QETH_PERF_PROCFILE_NAME, NULL); | |
| } | |