o tcpv4: convert /proc/net/tcp to seq_file

parent 3f4e581c
......@@ -1165,7 +1165,8 @@ extern void fib_proc_exit(void);
extern int ip_misc_proc_init(void);
extern int raw_proc_init(void);
extern void raw_proc_exit(void);
extern int tcp_get_info(char *buffer, char **start, off_t offset, int length);
extern int tcp_proc_init(void);
extern void tcp_proc_exit(void);
extern int udp_proc_init(void);
extern void udp_proc_exit(void);
......@@ -1175,7 +1176,7 @@ int __init ipv4_proc_init(void)
if (raw_proc_init())
goto out_raw;
if (!proc_net_create("tcp", 0, tcp_get_info))
if (tcp_proc_init())
goto out_tcp;
if (udp_proc_init())
goto out_udp;
......@@ -1190,7 +1191,7 @@ int __init ipv4_proc_init(void)
out_fib:
udp_proc_exit();
out_udp:
proc_net_remove("tcp");
tcp_proc_exit();
out_tcp:
raw_proc_exit();
out_raw:
......
......@@ -69,6 +69,8 @@
#include <linux/inet.h>
#include <linux/ipv6.h>
#include <linux/stddef.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
extern int sysctl_ip_dynaddr;
extern int sysctl_ip_default_ttl;
......@@ -2131,7 +2133,295 @@ static int tcp_v4_destroy_sock(struct sock *sk)
return 0;
}
#ifdef CONFIG_PROC_FS
/* Proc filesystem TCP sock list dumping. */
enum tcp_seq_states {
TCP_SEQ_STATE_LISTENING,
TCP_SEQ_STATE_OPENREQ,
TCP_SEQ_STATE_ESTABLISHED,
TCP_SEQ_STATE_TIME_WAIT,
};
struct tcp_iter_state {
enum tcp_seq_states state;
struct sock *syn_wait_sk;
int bucket, sbucket, num, uid;
};
static void *listening_get_first(struct seq_file *seq)
{
struct tcp_iter_state* st = seq->private;
void *rc = NULL;
for (st->bucket = 0; st->bucket < TCP_LHTABLE_SIZE; ++st->bucket) {
struct open_request *req;
struct tcp_opt *tp;
struct sock *sk = tcp_listening_hash[st->bucket];
if (!sk)
continue;
++st->num;
if (TCP_INET_FAMILY(sk->family)) {
rc = sk;
goto out;
}
tp = tcp_sk(sk);
read_lock_bh(&tp->syn_wait_lock);
if (tp->listen_opt && tp->listen_opt->qlen) {
st->uid = sock_i_uid(sk);
st->syn_wait_sk = sk;
st->state = TCP_SEQ_STATE_OPENREQ;
for (st->sbucket = 0; st->sbucket < TCP_SYNQ_HSIZE;
++st->sbucket) {
for (req = tp->listen_opt->syn_table[st->sbucket];
req; req = req->dl_next, ++st->num) {
if (!TCP_INET_FAMILY(req->class->family))
continue;
rc = req;
goto out;
}
}
st->state = TCP_SEQ_STATE_LISTENING;
}
read_unlock_bh(&tp->syn_wait_lock);
}
out:
return rc;
}
static void *listening_get_next(struct seq_file *seq, void *cur)
{
struct tcp_opt *tp;
struct sock *sk = cur;
struct tcp_iter_state* st = seq->private;
if (st->state == TCP_SEQ_STATE_OPENREQ) {
struct open_request *req = cur;
tp = tcp_sk(st->syn_wait_sk);
req = req->dl_next;
while (1) {
while (req) {
++st->num;
if (TCP_INET_FAMILY(req->class->family)) {
cur = req;
goto out;
}
req = req->dl_next;
}
if (++st->sbucket >= TCP_SYNQ_HSIZE)
break;
get_req:
req = tp->listen_opt->syn_table[st->sbucket];
}
sk = st->syn_wait_sk->next;
st->state = TCP_SEQ_STATE_LISTENING;
read_unlock_bh(&tp->syn_wait_lock);
} else
sk = sk->next;
get_sk:
while (sk) {
if (TCP_INET_FAMILY(sk->family)) {
cur = sk;
goto out;
}
tp = tcp_sk(sk);
read_lock_bh(&tp->syn_wait_lock);
if (tp->listen_opt && tp->listen_opt->qlen) {
st->uid = sock_i_uid(sk);
st->syn_wait_sk = sk;
st->state = TCP_SEQ_STATE_OPENREQ;
st->sbucket = 0;
goto get_req;
}
read_unlock_bh(&tp->syn_wait_lock);
}
if (++st->bucket < TCP_LHTABLE_SIZE) {
sk = tcp_listening_hash[st->bucket];
goto get_sk;
}
cur = NULL;
out:
return cur;
}
static void *listening_get_idx(struct seq_file *seq, loff_t *pos)
{
void *rc = listening_get_first(seq);
if (rc)
while (*pos && (rc = listening_get_next(seq, rc)))
--*pos;
return *pos ? NULL : rc;
}
static void *established_get_first(struct seq_file *seq)
{
struct tcp_iter_state* st = seq->private;
void *rc = NULL;
for (st->bucket = 0; st->bucket < tcp_ehash_size; ++st->bucket) {
struct sock *sk;
struct tcp_tw_bucket *tw;
read_lock(&tcp_ehash[st->bucket].lock);
for (sk = tcp_ehash[st->bucket].chain; sk;
sk = sk->next, ++st->num) {
if (!TCP_INET_FAMILY(sk->family))
continue;
rc = sk;
goto out;
}
st->state = TCP_SEQ_STATE_TIME_WAIT;
for (tw = (struct tcp_tw_bucket *)
tcp_ehash[st->bucket + tcp_ehash_size].chain;
tw; tw = (struct tcp_tw_bucket *)tw->next, ++st->num) {
if (!TCP_INET_FAMILY(tw->family))
continue;
rc = tw;
goto out;
}
read_unlock(&tcp_ehash[st->bucket].lock);
st->state = TCP_SEQ_STATE_ESTABLISHED;
}
out:
return rc;
}
static void *established_get_next(struct seq_file *seq, void *cur)
{
struct sock *sk = cur;
struct tcp_tw_bucket *tw;
struct tcp_iter_state* st = seq->private;
if (st->state == TCP_SEQ_STATE_TIME_WAIT) {
tw = cur;
tw = (struct tcp_tw_bucket *)tw->next;
get_tw:
while (tw && !TCP_INET_FAMILY(tw->family)) {
++st->num;
tw = (struct tcp_tw_bucket *)tw->next;
}
if (tw) {
cur = tw;
goto out;
}
read_unlock(&tcp_ehash[st->bucket].lock);
st->state = TCP_SEQ_STATE_ESTABLISHED;
if (++st->bucket < tcp_ehash_size) {
read_lock(&tcp_ehash[st->bucket].lock);
sk = tcp_ehash[st->bucket].chain;
} else {
cur = NULL;
goto out;
}
} else
sk = sk->next;
while (sk && !TCP_INET_FAMILY(sk->family)) {
++st->num;
sk = sk->next;
}
if (!sk) {
st->state = TCP_SEQ_STATE_TIME_WAIT;
tw = (struct tcp_tw_bucket *)
tcp_ehash[st->bucket + tcp_ehash_size].chain;
goto get_tw;
}
cur = sk;
out:
return cur;
}
static void *established_get_idx(struct seq_file *seq, loff_t pos)
{
void *rc = established_get_first(seq);
if (rc)
while (pos && (rc = established_get_next(seq, rc)))
--pos;
return pos ? NULL : rc;
}
static void *tcp_get_idx(struct seq_file *seq, loff_t pos)
{
void *rc;
struct tcp_iter_state* st = seq->private;
tcp_listen_lock();
st->state = TCP_SEQ_STATE_LISTENING;
rc = listening_get_idx(seq, &pos);
if (!rc) {
tcp_listen_unlock();
local_bh_disable();
st->state = TCP_SEQ_STATE_ESTABLISHED;
rc = established_get_idx(seq, pos);
}
return rc;
}
static void *tcp_seq_start(struct seq_file *seq, loff_t *pos)
{
return *pos ? tcp_get_idx(seq, *pos - 1) : (void *)1;
}
static void *tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
void *rc = NULL;
struct tcp_iter_state* st;
if (v == (void *)1) {
rc = tcp_get_idx(seq, 0);
goto out;
}
st = seq->private;
switch (st->state) {
case TCP_SEQ_STATE_OPENREQ:
case TCP_SEQ_STATE_LISTENING:
rc = listening_get_next(seq, v);
if (!rc) {
tcp_listen_unlock();
local_bh_disable();
st->state = TCP_SEQ_STATE_ESTABLISHED;
rc = established_get_first(seq);
}
break;
case TCP_SEQ_STATE_ESTABLISHED:
case TCP_SEQ_STATE_TIME_WAIT:
rc = established_get_next(seq, v);
break;
}
out:
++*pos;
return rc;
}
static void tcp_seq_stop(struct seq_file *seq, void *v)
{
struct tcp_iter_state* st = seq->private;
switch (st->state) {
case TCP_SEQ_STATE_OPENREQ:
if (v) {
struct tcp_opt *tp = tcp_sk(st->syn_wait_sk);
read_unlock_bh(&tp->syn_wait_lock);
}
case TCP_SEQ_STATE_LISTENING:
tcp_listen_unlock();
break;
case TCP_SEQ_STATE_TIME_WAIT:
case TCP_SEQ_STATE_ESTABLISHED:
if (v)
read_unlock(&tcp_ehash[st->bucket].lock);
local_bh_enable();
break;
}
}
static void get_openreq(struct sock *sk, struct open_request *req,
char *tmpbuf, int i, int uid)
{
......@@ -2219,137 +2509,89 @@ static void get_timewait_sock(struct tcp_tw_bucket *tw, char *tmpbuf, int i)
#define TMPSZ 150
int tcp_get_info(char *buffer, char **start, off_t offset, int length)
static int tcp_seq_show(struct seq_file *seq, void *v)
{
int len = 0, num = 0, i;
off_t begin, pos = 0;
struct tcp_iter_state* st;
char tmpbuf[TMPSZ + 1];
if (offset < TMPSZ)
len += sprintf(buffer, "%-*s\n", TMPSZ - 1,
if (v == (void *)1) {
seq_printf(seq, "%-*s\n", TMPSZ - 1,
" sl local_address rem_address st tx_queue "
"rx_queue tr tm->when retrnsmt uid timeout "
"inode");
pos = TMPSZ;
/* First, walk listening socket table. */
tcp_listen_lock();
for (i = 0; i < TCP_LHTABLE_SIZE; i++) {
struct sock *sk;
struct tcp_listen_opt *lopt;
int k;
for (sk = tcp_listening_hash[i]; sk; sk = sk->next, num++) {
struct open_request *req;
int uid;
struct tcp_opt *tp = tcp_sk(sk);
if (!TCP_INET_FAMILY(sk->family))
goto skip_listen;
pos += TMPSZ;
if (pos >= offset) {
get_tcp_sock(sk, tmpbuf, num);
len += sprintf(buffer + len, "%-*s\n",
TMPSZ - 1, tmpbuf);
if (pos >= offset + length) {
tcp_listen_unlock();
goto out_no_bh;
}
}
skip_listen:
uid = sock_i_uid(sk);
read_lock_bh(&tp->syn_wait_lock);
lopt = tp->listen_opt;
if (lopt && lopt->qlen) {
for (k = 0; k < TCP_SYNQ_HSIZE; k++) {
for (req = lopt->syn_table[k];
req; req = req->dl_next, num++) {
if (!TCP_INET_FAMILY(req->class->family))
continue;
pos += TMPSZ;
if (pos <= offset)
continue;
get_openreq(sk, req, tmpbuf,
num, uid);
len += sprintf(buffer + len,
"%-*s\n",
TMPSZ - 1,
tmpbuf);
if (pos >= offset + length) {
read_unlock_bh(&tp->syn_wait_lock);
tcp_listen_unlock();
goto out_no_bh;
}
}
}
goto out;
}
read_unlock_bh(&tp->syn_wait_lock);
st = seq->private;
/* Completed requests are in normal socket hash table */
}
switch (st->state) {
case TCP_SEQ_STATE_LISTENING:
case TCP_SEQ_STATE_ESTABLISHED:
get_tcp_sock(v, tmpbuf, st->num);
break;
case TCP_SEQ_STATE_OPENREQ:
get_openreq(st->syn_wait_sk, v, tmpbuf, st->num, st->uid);
break;
case TCP_SEQ_STATE_TIME_WAIT:
get_timewait_sock(v, tmpbuf, st->num);
break;
}
tcp_listen_unlock();
seq_printf(seq, "%-*s\n", TMPSZ - 1, tmpbuf);
out:
return 0;
}
local_bh_disable();
static struct seq_operations tcp_seq_ops = {
.start = tcp_seq_start,
.next = tcp_seq_next,
.stop = tcp_seq_stop,
.show = tcp_seq_show,
};
/* Next, walk established hash chain. */
for (i = 0; i < tcp_ehash_size; i++) {
struct tcp_ehash_bucket *head = &tcp_ehash[i];
struct sock *sk;
struct tcp_tw_bucket *tw;
static int tcp_seq_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
int rc = -ENOMEM;
struct tcp_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
read_lock(&head->lock);
for (sk = head->chain; sk; sk = sk->next, num++) {
if (!TCP_INET_FAMILY(sk->family))
continue;
pos += TMPSZ;
if (pos <= offset)
continue;
get_tcp_sock(sk, tmpbuf, num);
len += sprintf(buffer + len, "%-*s\n",
TMPSZ - 1, tmpbuf);
if (pos >= offset + length) {
read_unlock(&head->lock);
if (!s)
goto out;
}
}
for (tw = (struct tcp_tw_bucket *)tcp_ehash[i +
tcp_ehash_size].chain;
tw;
tw = (struct tcp_tw_bucket *)tw->next, num++) {
if (!TCP_INET_FAMILY(tw->family))
continue;
pos += TMPSZ;
if (pos <= offset)
continue;
get_timewait_sock(tw, tmpbuf, num);
len += sprintf(buffer + len, "%-*s\n",
TMPSZ - 1, tmpbuf);
if (pos >= offset + length) {
read_unlock(&head->lock);
rc = seq_open(file, &tcp_seq_ops);
if (rc)
goto out_kfree;
seq = file->private_data;
seq->private = s;
memset(s, 0, sizeof(*s));
out:
return rc;
out_kfree:
kfree(s);
goto out;
}
}
read_unlock(&head->lock);
}
}
out:
local_bh_enable();
out_no_bh:
static struct file_operations tcp_seq_fops = {
.open = tcp_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = ip_seq_release,
};
begin = len - (pos - offset);
*start = buffer + begin;
len -= begin;
if (len > length)
len = length;
if (len < 0)
len = 0;
return len;
int __init tcp_proc_init(void)
{
int rc = 0;
struct proc_dir_entry *p = create_proc_entry("tcp", S_IRUGO, proc_net);
if (p)
p->proc_fops = &tcp_seq_fops;
else
rc = -ENOMEM;
return rc;
}
void __init tcp_proc_exit(void)
{
remove_proc_entry("tcp", proc_net);
}
#endif /* CONFIG_PROC_FS */
struct proto tcp_prot = {
.name = "TCP",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment