[NET] Add proc file to display the state of all qdiscs V2 TC is a complicated tool and it currently does not allow the display of all qdisc states. Qdisc information is necessary in order to figure out where packet loss occurs. The information included here also contains the current operating state. One can see what the current state of the qdisc queues are. This patch adds /proc/net/qdisc_stats which displays the current state of all qdiscs on the system. F.e. V1->V2: - Use q->qlen instead of q->qstats.qlen to display queue length. - Increase field width. Signed-off-by: Christoph Lameter --- net/sched/sch_api.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) Index: linux-2.6/net/sched/sch_api.c =================================================================== --- linux-2.6.orig/net/sched/sch_api.c 2009-09-18 06:49:37.000000000 -0500 +++ linux-2.6/net/sched/sch_api.c 2009-09-29 09:01:44.000000000 -0500 @@ -1700,6 +1700,135 @@ static const struct file_operations psch .llseek = seq_lseek, .release = single_release, }; + +static void dump_qdisc(struct seq_file *seq, struct net_device *dev, + struct Qdisc *q, char *inout, char *text) +{ + char state[5]; + char *p = state; + + if (test_bit(__QDISC_STATE_RUNNING, &q->state)) + *p++ = 'R'; + if (test_bit(__QDISC_STATE_SCHED, &q->state)) + *p++ = 'S'; + if (test_bit(__QDISC_STATE_DEACTIVATED, &q->state)) + *p++ = 'D'; + if (q->state == 0) + *p++ = '-'; + *p = 0; + + seq_printf(seq, "%3s/%2s %6s %3s %10lld %8d %7d %7d %7d %7d %7d\n", + inout, text, dev->name, state, + q->bstats.bytes, q->bstats.packets, + q->q.qlen, q->qstats.backlog, q->qstats.drops, + q->qstats.requeues, q->qstats.overlimits); +} + +static void dump_qdisc_root(struct seq_file *seq, struct net_device *dev, + struct Qdisc *root, char *inout) +{ + struct Qdisc *q; + int n = 0; + + if (!root) + return; + + dump_qdisc(seq, dev, root, inout, "root"); + + list_for_each_entry(q, &root->list, list) { + char buffer[10]; + + sprintf(buffer,"Q%d", ++n); + dump_qdisc(seq, dev, q, inout, buffer); + } +} + + +static void qdisc_seq_out(struct seq_file *seq, struct net_device *dev) +{ + struct netdev_queue *dev_queue; + int i; + + for (i = 0; i < dev->real_num_tx_queues; i++) { + char buffer[10]; + + dev_queue = netdev_get_tx_queue(dev, i); + sprintf(buffer, "TX%d", i); + dump_qdisc_root(seq, dev, dev_queue->qdisc_sleeping, buffer); + } + dev_queue = &dev->rx_queue; + dump_qdisc_root(seq, dev, dev_queue->qdisc_sleeping, "RX"); +} + +static int qdisc_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_printf(seq, "Queue Device State Bytes Packets " + " Qlen Backlog Drops Requeue Overlimit\n"); + else + qdisc_seq_out(seq, v); + + return 0; +} + +static void *qdisc_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(dev_base_lock) +{ + struct net *net = seq_file_net(seq); + loff_t off; + struct net_device *dev; + + read_lock(&dev_base_lock); + + if (!*pos) + return SEQ_START_TOKEN; + + off = 1; + + for_each_netdev(net, dev) + if (off++ == *pos) + return dev; + + return NULL; +} + +static void *qdisc_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct net *net = seq_file_net(seq); + + ++*pos; + return v == SEQ_START_TOKEN ? + first_net_device(net) : next_net_device((struct net_device *)v); +} + +static void qdisc_seq_stop(struct seq_file *seq, void *v) + __releases(dev_base_lock) +{ + read_unlock(&dev_base_lock); +} + + +static const struct seq_operations qdisc_seq_ops = { + .start = qdisc_seq_start, + .next = qdisc_seq_next, + .stop = qdisc_seq_stop, + .show = qdisc_seq_show, +}; + +static int qdisc_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &qdisc_seq_ops, + sizeof(struct seq_net_private)); +} + + +static const struct file_operations qdisc_fops = { + .owner = THIS_MODULE, + .open = qdisc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; #endif static int __init pktsched_init(void) @@ -1708,6 +1837,7 @@ static int __init pktsched_init(void) register_qdisc(&bfifo_qdisc_ops); register_qdisc(&mq_qdisc_ops); proc_net_fops_create(&init_net, "psched", 0, &psched_fops); + proc_net_fops_create(&init_net, "qdisc_stats", 0, &qdisc_fops); rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL); rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL);