|  | /* | 
|  | * | 
|  | * 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 const 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 const 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); | 
|  | } | 
|  |  |