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