| /* |
| * |
| * linux/drivers/s390/net/qeth_fs.c ($Revision: 1.13 $) |
| * |
| * 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" |
| |
| const char *VERSION_QETH_PROC_C = "$Revision: 1.13 $"; |
| |
| /***** /proc/qeth *****/ |
| #define QETH_PROCFILE_NAME "qeth" |
| static struct proc_dir_entry *qeth_procfile; |
| |
| static void * |
| qeth_procfile_seq_start(struct seq_file *s, loff_t *offset) |
| { |
| struct list_head *next_card = NULL; |
| int i = 0; |
| |
| down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); |
| |
| if (*offset == 0) |
| return SEQ_START_TOKEN; |
| |
| /* get card at pos *offset */ |
| list_for_each(next_card, &qeth_ccwgroup_driver.driver.devices) |
| if (++i == *offset) |
| return next_card; |
| |
| return NULL; |
| } |
| |
| 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 list_head *next_card = NULL; |
| struct list_head *current_card; |
| |
| if (it == SEQ_START_TOKEN) { |
| next_card = qeth_ccwgroup_driver.driver.devices.next; |
| if (next_card->next == next_card) /* list empty */ |
| return NULL; |
| (*offset)++; |
| } else { |
| current_card = (struct list_head *)it; |
| if (current_card->next == &qeth_ccwgroup_driver.driver.devices) |
| return NULL; /* end of list reached */ |
| next_card = current_card->next; |
| (*offset)++; |
| } |
| |
| return next_card; |
| } |
| |
| static inline const char * |
| qeth_get_router_str(struct qeth_card *card, int ipv) |
| { |
| int routing_type = 0; |
| |
| 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 */ |
| } |
| |
| if (routing_type == PRIMARY_ROUTER) |
| return "pri"; |
| else if (routing_type == SECONDARY_ROUTER) |
| return "sec"; |
| else if (routing_type == MULTICAST_ROUTER) { |
| if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) |
| return "mc+"; |
| return "mc"; |
| } else if (routing_type == PRIMARY_CONNECTOR) { |
| if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) |
| return "p+c"; |
| return "p.c"; |
| } else if (routing_type == SECONDARY_CONNECTOR) { |
| if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) |
| return "s+c"; |
| return "s.c"; |
| } else if (routing_type == NO_ROUTER) |
| return "no"; |
| else |
| return "unk"; |
| } |
| |
| 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 = list_entry(it, struct device, driver_list); |
| 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"); |
| } |
| 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; |
| |
| #ifdef CONFIG_QETH_PERF_STATS |
| |
| static void * |
| qeth_perf_procfile_seq_start(struct seq_file *s, loff_t *offset) |
| { |
| struct list_head *next_card = NULL; |
| int i = 0; |
| |
| down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); |
| /* get card at pos *offset */ |
| list_for_each(next_card, &qeth_ccwgroup_driver.driver.devices){ |
| if (i == *offset) |
| return next_card; |
| i++; |
| } |
| return NULL; |
| } |
| |
| static void |
| qeth_perf_procfile_seq_stop(struct seq_file *s, void* it) |
| { |
| up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); |
| } |
| |
| static void * |
| qeth_perf_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset) |
| { |
| struct list_head *current_card = (struct list_head *)it; |
| |
| if (current_card->next == &qeth_ccwgroup_driver.driver.devices) |
| return NULL; /* end of list reached */ |
| (*offset)++; |
| return current_card->next; |
| } |
| |
| static int |
| qeth_perf_procfile_seq_show(struct seq_file *s, void *it) |
| { |
| struct device *device; |
| struct qeth_card *card; |
| |
| device = list_entry(it, struct device, driver_list); |
| 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) |
| ); |
| seq_printf(s, " Skb's/buffers received : %li/%i\n" |
| " Skb's/buffers sent : %li/%i\n\n", |
| card->stats.rx_packets, card->perf_stats.bufs_rec, |
| card->stats.tx_packets, card->perf_stats.bufs_sent |
| ); |
| seq_printf(s, " Skb's/buffers sent without packing : %li/%i\n" |
| " Skb's/buffers sent with packing : %i/%i\n\n", |
| card->stats.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 : %i\n" |
| " Skb fragments sent in SG mode : %i\n\n", |
| card->perf_stats.sg_skbs_sent, |
| card->perf_stats.sg_frags_sent); |
| seq_printf(s, " large_send tx (in Kbytes) : %i\n" |
| " large_send count : %i\n\n", |
| card->perf_stats.large_send_bytes >> 10, |
| card->perf_stats.large_send_cnt); |
| seq_printf(s, " Packing state changes no pkg.->packing : %i/%i\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) : %i\n" |
| " Inbound handler count : %i\n" |
| " Inbound do_QDIO time (in us) : %i\n" |
| " Inbound do_QDIO count : %i\n\n" |
| " Outbound handler time (in us) : %i\n" |
| " Outbound handler count : %i\n\n" |
| " Outbound time (in us, incl QDIO) : %i\n" |
| " Outbound count : %i\n" |
| " Outbound do_QDIO time (in us) : %i\n" |
| " Outbound do_QDIO count : %i\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 |
| ); |
| return 0; |
| } |
| |
| static struct seq_operations qeth_perf_procfile_seq_ops = { |
| .start = qeth_perf_procfile_seq_start, |
| .stop = qeth_perf_procfile_seq_stop, |
| .next = qeth_perf_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, |
| }; |
| |
| #define qeth_perf_procfile_created qeth_perf_procfile |
| #else |
| #define qeth_perf_procfile_created 1 |
| #endif /* CONFIG_QETH_PERF_STATS */ |
| |
| /***** /proc/qeth_ipa_takeover *****/ |
| #define QETH_IPATO_PROCFILE_NAME "qeth_ipa_takeover" |
| static struct proc_dir_entry *qeth_ipato_procfile; |
| |
| static void * |
| qeth_ipato_procfile_seq_start(struct seq_file *s, loff_t *offset) |
| { |
| struct list_head *next_card = NULL; |
| int i = 0; |
| |
| down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); |
| /* TODO: finish this */ |
| /* |
| * maybe SEQ_SATRT_TOKEN can be returned for offset 0 |
| * output driver settings then; |
| * else output setting for respective card |
| */ |
| /* get card at pos *offset */ |
| list_for_each(next_card, &qeth_ccwgroup_driver.driver.devices){ |
| if (i == *offset) |
| return next_card; |
| i++; |
| } |
| return NULL; |
| } |
| |
| static void |
| qeth_ipato_procfile_seq_stop(struct seq_file *s, void* it) |
| { |
| up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); |
| } |
| |
| static void * |
| qeth_ipato_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset) |
| { |
| struct list_head *current_card = (struct list_head *)it; |
| |
| /* TODO: finish this */ |
| /* |
| * maybe SEQ_SATRT_TOKEN can be returned for offset 0 |
| * output driver settings then; |
| * else output setting for respective card |
| */ |
| if (current_card->next == &qeth_ccwgroup_driver.driver.devices) |
| return NULL; /* end of list reached */ |
| (*offset)++; |
| return current_card->next; |
| } |
| |
| static int |
| qeth_ipato_procfile_seq_show(struct seq_file *s, void *it) |
| { |
| struct device *device; |
| struct qeth_card *card; |
| |
| /* TODO: finish this */ |
| /* |
| * maybe SEQ_SATRT_TOKEN can be returned for offset 0 |
| * output driver settings then; |
| * else output setting for respective card |
| */ |
| device = list_entry(it, struct device, driver_list); |
| card = device->driver_data; |
| |
| return 0; |
| } |
| |
| static struct seq_operations qeth_ipato_procfile_seq_ops = { |
| .start = qeth_ipato_procfile_seq_start, |
| .stop = qeth_ipato_procfile_seq_stop, |
| .next = qeth_ipato_procfile_seq_next, |
| .show = qeth_ipato_procfile_seq_show, |
| }; |
| |
| static int |
| qeth_ipato_procfile_open(struct inode *inode, struct file *file) |
| { |
| return seq_open(file, &qeth_ipato_procfile_seq_ops); |
| } |
| |
| static struct file_operations qeth_ipato_procfile_fops = { |
| .owner = THIS_MODULE, |
| .open = qeth_ipato_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; |
| |
| #ifdef CONFIG_QETH_PERF_STATS |
| 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; |
| #endif /* CONFIG_QETH_PERF_STATS */ |
| |
| qeth_ipato_procfile = create_proc_entry(QETH_IPATO_PROCFILE_NAME, |
| S_IFREG | 0444, NULL); |
| if (qeth_ipato_procfile) |
| qeth_ipato_procfile->proc_fops = &qeth_ipato_procfile_fops; |
| |
| if (qeth_procfile && |
| qeth_ipato_procfile && |
| qeth_perf_procfile_created) |
| 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); |
| if (qeth_ipato_procfile) |
| remove_proc_entry(QETH_IPATO_PROCFILE_NAME, NULL); |
| } |
| |
| |
| /* ONLY FOR DEVELOPMENT! -> make it as module */ |
| /* |
| static void |
| qeth_create_sysfs_entries(void) |
| { |
| struct device *dev; |
| |
| down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); |
| |
| list_for_each_entry(dev, &qeth_ccwgroup_driver.driver.devices, |
| driver_list) |
| qeth_create_device_attributes(dev); |
| |
| up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); |
| } |
| |
| static void |
| qeth_remove_sysfs_entries(void) |
| { |
| struct device *dev; |
| |
| down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); |
| |
| list_for_each_entry(dev, &qeth_ccwgroup_driver.driver.devices, |
| driver_list) |
| qeth_remove_device_attributes(dev); |
| |
| up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); |
| } |
| |
| static int __init |
| qeth_fs_init(void) |
| { |
| printk(KERN_INFO "qeth_fs_init\n"); |
| qeth_create_procfs_entries(); |
| qeth_create_sysfs_entries(); |
| |
| return 0; |
| } |
| |
| static void __exit |
| qeth_fs_exit(void) |
| { |
| printk(KERN_INFO "qeth_fs_exit\n"); |
| qeth_remove_procfs_entries(); |
| qeth_remove_sysfs_entries(); |
| } |
| |
| |
| module_init(qeth_fs_init); |
| module_exit(qeth_fs_exit); |
| |
| MODULE_LICENSE("GPL"); |
| */ |