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