Commit 0e80ab36 authored by David S. Miller's avatar David S. Miller

Merge master.kernel.org:/home/acme/BK/net-2.5

into nuts.ninka.net:/home/davem/src/BK/net-2.5
parents 5b47557d b53a0ade
...@@ -126,6 +126,8 @@ struct igmpmsg ...@@ -126,6 +126,8 @@ struct igmpmsg
*/ */
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <net/sock.h>
extern int ip_mroute_setsockopt(struct sock *, int, char *, int); extern int ip_mroute_setsockopt(struct sock *, int, char *, int);
extern int ip_mroute_getsockopt(struct sock *, int, char *, int *); extern int ip_mroute_getsockopt(struct sock *, int, char *, int *);
extern int ipmr_ioctl(struct sock *sk, int cmd, unsigned long arg); extern int ipmr_ioctl(struct sock *sk, int cmd, unsigned long arg);
......
...@@ -94,6 +94,7 @@ ...@@ -94,6 +94,7 @@
#include <net/sock.h> #include <net/sock.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <linux/divert.h> #include <linux/divert.h>
...@@ -114,6 +115,7 @@ ...@@ -114,6 +115,7 @@
extern int plip_init(void); extern int plip_init(void);
#endif #endif
#include <asm/current.h>
/* This define, if set, will randomly drop a packet when congestion /* This define, if set, will randomly drop a packet when congestion
* is more than moderate. It helps fairness in the multi-interface * is more than moderate. It helps fairness in the multi-interface
...@@ -1719,128 +1721,196 @@ static int dev_ifconf(char *arg) ...@@ -1719,128 +1721,196 @@ static int dev_ifconf(char *arg)
return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0; return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0;
} }
#ifdef CONFIG_PROC_FS
/* /*
* This is invoked by the /proc filesystem handler to display a device * This is invoked by the /proc filesystem handler to display a device
* in detail. * in detail.
*/ */
static __inline__ struct net_device *dev_get_idx(struct seq_file *seq,
loff_t pos)
{
struct net_device *dev;
loff_t i;
#ifdef CONFIG_PROC_FS for (i = 0, dev = dev_base; dev && i < pos; dev = dev->next);
return i == pos ? dev : NULL;
}
void *dev_seq_start(struct seq_file *seq, loff_t *pos)
{
read_lock(&dev_base_lock);
return *pos ? dev_get_idx(seq, *pos) : (void *)1;
}
static int sprintf_stats(char *buffer, struct net_device *dev) void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
return v == (void *)1 ? dev_base : ((struct net_device *)v)->next;
}
void dev_seq_stop(struct seq_file *seq, void *v)
{
read_unlock(&dev_base_lock);
}
static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
{ {
struct net_device_stats *stats = dev->get_stats ? dev->get_stats(dev) : struct net_device_stats *stats = dev->get_stats ? dev->get_stats(dev) :
NULL; NULL;
int size;
if (stats) if (stats)
size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu " seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu "
"%10lu %9lu %8lu %7lu %4lu %4lu %4lu " "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
"%5lu %7lu %10lu\n", dev->name, stats->rx_bytes, stats->rx_packets,
dev->name, stats->rx_errors,
stats->rx_bytes, stats->rx_dropped + stats->rx_missed_errors,
stats->rx_packets, stats->rx_errors, stats->rx_fifo_errors,
stats->rx_dropped + stats->rx_missed_errors, stats->rx_length_errors + stats->rx_over_errors +
stats->rx_fifo_errors, stats->rx_crc_errors + stats->rx_frame_errors,
stats->rx_length_errors + stats->rx_over_errors + stats->rx_compressed, stats->multicast,
stats->rx_crc_errors + stats->rx_frame_errors, stats->tx_bytes, stats->tx_packets,
stats->rx_compressed, stats->multicast, stats->tx_errors, stats->tx_dropped,
stats->tx_bytes, stats->tx_fifo_errors, stats->collisions,
stats->tx_packets, stats->tx_errors, stats->tx_dropped, stats->tx_carrier_errors +
stats->tx_fifo_errors, stats->collisions, stats->tx_aborted_errors +
stats->tx_carrier_errors + stats->tx_aborted_errors + stats->tx_window_errors +
stats->tx_window_errors + stats->tx_heartbeat_errors, stats->tx_heartbeat_errors,
stats->tx_compressed); stats->tx_compressed);
else else
size = sprintf(buffer, "%6s: No statistics available.\n", seq_printf(seq, "%6s: No statistics available.\n", dev->name);
dev->name);
return size;
} }
/* /*
* Called from the PROCfs module. This now uses the new arbitrary sized * Called from the PROCfs module. This now uses the new arbitrary sized
* /proc/net interface to create /proc/net/dev * /proc/net interface to create /proc/net/dev
*/ */
static int dev_get_info(char *buffer, char **start, off_t offset, int length) static int dev_seq_show(struct seq_file *seq, void *v)
{ {
int len = 0; if (v == (void *)1)
off_t begin = 0; seq_printf(seq, "Inter-| Receive "
off_t pos = 0; " | Transmit\n"
int size; " face |bytes packets errs drop fifo frame "
struct net_device *dev; "compressed multicast|bytes packets errs "
"drop fifo colls carrier compressed\n");
else
dev_seq_printf_stats(seq, v);
return 0;
}
size = sprintf(buffer, static struct netif_rx_stats *softnet_get_online(loff_t *pos)
"Inter-| Receive | Transmit\n" {
" face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n"); struct netif_rx_stats *rc = NULL;
pos += size; while (*pos < NR_CPUS)
len += size; if (cpu_online(*pos)) {
rc = &netdev_rx_stat[*pos];
break;
} else
++*pos;
return rc;
}
read_lock(&dev_base_lock); static void *softnet_seq_start(struct seq_file *seq, loff_t *pos)
for (dev = dev_base; dev; dev = dev->next) { {
size = sprintf_stats(buffer+len, dev); return softnet_get_online(pos);
len += size; }
pos = begin + len;
if (pos < offset) { static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
len = 0; {
begin = pos; ++*pos;
} return softnet_get_online(pos);
if (pos > offset + length) }
break;
}
read_unlock(&dev_base_lock);
*start = buffer + (offset - begin); /* Start of wanted data */ static void softnet_seq_stop(struct seq_file *seq, void *v)
len -= offset - begin; /* Start slop */ {
if (len > length)
len = length; /* Ending slop */
if (len < 0)
len = 0;
return len;
} }
static int dev_proc_stats(char *buffer, char **start, off_t offset, static int softnet_seq_show(struct seq_file *seq, void *v)
int length, int *eof, void *data)
{ {
int i; struct netif_rx_stats *s = v;
int len = 0;
for (i = 0; i < NR_CPUS; i++) { seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
if (!cpu_online(i)) s->total, s->dropped, s->time_squeeze, s->throttled,
continue; s->fastroute_hit, s->fastroute_success, s->fastroute_defer,
s->fastroute_deferred_out,
len += sprintf(buffer + len, "%08x %08x %08x %08x %08x %08x "
"%08x %08x %08x\n",
netdev_rx_stat[i].total,
netdev_rx_stat[i].dropped,
netdev_rx_stat[i].time_squeeze,
netdev_rx_stat[i].throttled,
netdev_rx_stat[i].fastroute_hit,
netdev_rx_stat[i].fastroute_success,
netdev_rx_stat[i].fastroute_defer,
netdev_rx_stat[i].fastroute_deferred_out,
#if 0 #if 0
netdev_rx_stat[i].fastroute_latency_reduction s->fastroute_latency_reduction
#else #else
netdev_rx_stat[i].cpu_collision s->cpu_collision
#endif #endif
); );
} return 0;
}
len -= offset; static struct seq_operations dev_seq_ops = {
.start = dev_seq_start,
.next = dev_seq_next,
.stop = dev_seq_stop,
.show = dev_seq_show,
};
if (len > length) static int dev_seq_open(struct inode *inode, struct file *file)
len = length; {
if (len < 0) return seq_open(file, &dev_seq_ops);
len = 0; }
static struct file_operations dev_seq_fops = {
.open = dev_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
*start = buffer + offset; static struct seq_operations softnet_seq_ops = {
*eof = 1; .start = softnet_seq_start,
.next = softnet_seq_next,
.stop = softnet_seq_stop,
.show = softnet_seq_show,
};
return len; static int softnet_seq_open(struct inode *inode, struct file *file)
{
return seq_open(file, &softnet_seq_ops);
} }
static struct file_operations softnet_seq_fops = {
.open = softnet_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#ifdef WIRELESS_EXT
extern int wireless_proc_init(void);
#else
#define wireless_proc_init() 0
#endif
static int __init dev_proc_init(void)
{
struct proc_dir_entry *p;
int rc = -ENOMEM;
p = create_proc_entry("dev", S_IRUGO, proc_net);
if (!p)
goto out;
p->proc_fops = &dev_seq_fops;
p = create_proc_entry("softnet_stat", S_IRUGO, proc_net);
if (!p)
goto out_dev;
p->proc_fops = &softnet_seq_fops;
if (wireless_proc_init())
goto out_softnet;
rc = 0;
out:
return rc;
out_softnet:
remove_proc_entry("softnet_stat", proc_net);
out_dev:
remove_proc_entry("dev", proc_net);
goto out;
}
#else
#define dev_proc_init() 0
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
...@@ -2680,10 +2750,13 @@ extern void dv_init(void); ...@@ -2680,10 +2750,13 @@ extern void dv_init(void);
static int __init net_dev_init(void) static int __init net_dev_init(void)
{ {
struct net_device *dev, **dp; struct net_device *dev, **dp;
int i; int i, rc = -ENOMEM;
BUG_ON(!dev_boot_phase); BUG_ON(!dev_boot_phase);
if (dev_proc_init())
goto out;
#ifdef CONFIG_NET_DIVERT #ifdef CONFIG_NET_DIVERT
dv_init(); dv_init();
#endif /* CONFIG_NET_DIVERT */ #endif /* CONFIG_NET_DIVERT */
...@@ -2787,15 +2860,6 @@ static int __init net_dev_init(void) ...@@ -2787,15 +2860,6 @@ static int __init net_dev_init(void)
} }
} }
#ifdef CONFIG_PROC_FS
proc_net_create("dev", 0, dev_get_info);
create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL);
#ifdef WIRELESS_EXT
/* Available in net/core/wireless.c */
proc_net_create("wireless", 0, dev_get_wireless_info);
#endif /* WIRELESS_EXT */
#endif /* CONFIG_PROC_FS */
dev_boot_phase = 0; dev_boot_phase = 0;
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
...@@ -2812,8 +2876,9 @@ static int __init net_dev_init(void) ...@@ -2812,8 +2876,9 @@ static int __init net_dev_init(void)
*/ */
net_device_init(); net_device_init();
rc = 0;
return 0; out:
return rc;
} }
subsys_initcall(net_dev_init); subsys_initcall(net_dev_init);
......
...@@ -47,15 +47,18 @@ ...@@ -47,15 +47,18 @@
/***************************** INCLUDES *****************************/ /***************************** INCLUDES *****************************/
#include <asm/uaccess.h> /* copy_to_user() */
#include <linux/config.h> /* Not needed ??? */ #include <linux/config.h> /* Not needed ??? */
#include <linux/types.h> /* off_t */ #include <linux/types.h> /* off_t */
#include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */ #include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */
#include <linux/proc_fs.h>
#include <linux/rtnetlink.h> /* rtnetlink stuff */ #include <linux/rtnetlink.h> /* rtnetlink stuff */
#include <linux/seq_file.h>
#include <linux/wireless.h> /* Pretty obvious */ #include <linux/wireless.h> /* Pretty obvious */
#include <net/iw_handler.h> /* New driver API */ #include <net/iw_handler.h> /* New driver API */
#include <asm/uaccess.h> /* copy_to_user() */
/**************************** CONSTANTS ****************************/ /**************************** CONSTANTS ****************************/
/* Enough lenience, let's make sure things are proper... */ /* Enough lenience, let's make sure things are proper... */
...@@ -330,83 +333,78 @@ static inline int get_priv_size(__u16 args) ...@@ -330,83 +333,78 @@ static inline int get_priv_size(__u16 args)
/* /*
* Print one entry (line) of /proc/net/wireless * Print one entry (line) of /proc/net/wireless
*/ */
static inline int sprintf_wireless_stats(char *buffer, struct net_device *dev) static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
struct net_device *dev)
{ {
/* Get stats from the driver */ /* Get stats from the driver */
struct iw_statistics *stats; struct iw_statistics *stats = get_wireless_stats(dev);
int size;
if (stats) {
stats = get_wireless_stats(dev); seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d "
if (stats != (struct iw_statistics *) NULL) { "%6d %6d %6d\n",
size = sprintf(buffer, dev->name, stats->status, stats->qual.qual,
"%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d %6d %6d %6d\n", stats->qual.updated & 1 ? '.' : ' ',
dev->name, ((__u8) stats->qual.level),
stats->status, stats->qual.updated & 2 ? '.' : ' ',
stats->qual.qual, ((__u8) stats->qual.noise),
stats->qual.updated & 1 ? '.' : ' ', stats->qual.updated & 4 ? '.' : ' ',
((__u8) stats->qual.level), stats->discard.nwid, stats->discard.code,
stats->qual.updated & 2 ? '.' : ' ', stats->discard.fragment, stats->discard.retries,
((__u8) stats->qual.noise), stats->discard.misc, stats->miss.beacon);
stats->qual.updated & 4 ? '.' : ' ',
stats->discard.nwid,
stats->discard.code,
stats->discard.fragment,
stats->discard.retries,
stats->discard.misc,
stats->miss.beacon);
stats->qual.updated = 0; stats->qual.updated = 0;
} }
else
size = 0;
return size;
} }
/* ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- */
/* /*
* Print info for /proc/net/wireless (print all entries) * Print info for /proc/net/wireless (print all entries)
*/ */
int dev_get_wireless_info(char * buffer, char **start, off_t offset, static int wireless_seq_show(struct seq_file *seq, void *v)
int length)
{ {
int len = 0; if (v == (void *)1)
off_t begin = 0; seq_printf(seq, "Inter-| sta-| Quality | Discarded "
off_t pos = 0; "packets | Missed\n"
int size; " face | tus | link level noise | nwid "
"crypt frag retry misc | beacon\n");
struct net_device * dev; else
wireless_seq_printf_stats(seq, v);
size = sprintf(buffer, return 0;
"Inter-| sta-| Quality | Discarded packets | Missed\n" }
" face | tus | link level noise | nwid crypt frag retry misc | beacon\n"
); extern void *dev_seq_start(struct seq_file *seq, loff_t *pos);
extern void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos);
pos += size; extern void dev_seq_stop(struct seq_file *seq, void *v);
len += size;
static struct seq_operations wireless_seq_ops = {
read_lock(&dev_base_lock); .start = dev_seq_start,
for (dev = dev_base; dev != NULL; dev = dev->next) { .next = dev_seq_next,
size = sprintf_wireless_stats(buffer + len, dev); .stop = dev_seq_stop,
len += size; .show = wireless_seq_show,
pos = begin + len; };
if (pos < offset) {
len = 0;
begin = pos;
}
if (pos > offset + length)
break;
}
read_unlock(&dev_base_lock);
*start = buffer + (offset - begin); /* Start of wanted data */ static int wireless_seq_open(struct inode *inode, struct file *file)
len -= (offset - begin); /* Start slop */ {
if (len > length) return seq_open(file, &wireless_seq_ops);
len = length; /* Ending slop */ }
if (len < 0)
len = 0; static struct file_operations wireless_seq_fops = {
.open = wireless_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
return len; int __init wireless_proc_init(void)
{
struct proc_dir_entry *p;
int rc = 0;
p = create_proc_entry("wireless", S_IRUGO, proc_net);
if (p)
p->proc_fops = &wireless_seq_fops;
else
rc = -ENOMEM;
return rc;
} }
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
......
...@@ -103,8 +103,6 @@ ...@@ -103,8 +103,6 @@
#include <net/tcp.h> #include <net/tcp.h>
#include <net/udp.h> #include <net/udp.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/raw.h> #include <net/raw.h>
#include <net/icmp.h> #include <net/icmp.h>
...@@ -1165,7 +1163,8 @@ extern void fib_proc_exit(void); ...@@ -1165,7 +1163,8 @@ extern void fib_proc_exit(void);
extern int ip_misc_proc_init(void); extern int ip_misc_proc_init(void);
extern int raw_proc_init(void); extern int raw_proc_init(void);
extern void raw_proc_exit(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 int udp_proc_init(void);
extern void udp_proc_exit(void); extern void udp_proc_exit(void);
...@@ -1175,7 +1174,7 @@ int __init ipv4_proc_init(void) ...@@ -1175,7 +1174,7 @@ int __init ipv4_proc_init(void)
if (raw_proc_init()) if (raw_proc_init())
goto out_raw; goto out_raw;
if (!proc_net_create("tcp", 0, tcp_get_info)) if (tcp_proc_init())
goto out_tcp; goto out_tcp;
if (udp_proc_init()) if (udp_proc_init())
goto out_udp; goto out_udp;
...@@ -1190,7 +1189,7 @@ int __init ipv4_proc_init(void) ...@@ -1190,7 +1189,7 @@ int __init ipv4_proc_init(void)
out_fib: out_fib:
udp_proc_exit(); udp_proc_exit();
out_udp: out_udp:
proc_net_remove("tcp"); tcp_proc_exit();
out_tcp: out_tcp:
raw_proc_exit(); raw_proc_exit();
out_raw: out_raw:
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#include <linux/sockios.h> #include <linux/sockios.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/mroute.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/in_route.h> #include <linux/in_route.h>
#include <linux/route.h> #include <linux/route.h>
......
...@@ -69,6 +69,8 @@ ...@@ -69,6 +69,8 @@
#include <linux/inet.h> #include <linux/inet.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
extern int sysctl_ip_dynaddr; extern int sysctl_ip_dynaddr;
extern int sysctl_ip_default_ttl; extern int sysctl_ip_default_ttl;
...@@ -2131,7 +2133,295 @@ static int tcp_v4_destroy_sock(struct sock *sk) ...@@ -2131,7 +2133,295 @@ static int tcp_v4_destroy_sock(struct sock *sk)
return 0; return 0;
} }
#ifdef CONFIG_PROC_FS
/* Proc filesystem TCP sock list dumping. */ /* 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, static void get_openreq(struct sock *sk, struct open_request *req,
char *tmpbuf, int i, int uid) char *tmpbuf, int i, int uid)
{ {
...@@ -2219,137 +2509,89 @@ static void get_timewait_sock(struct tcp_tw_bucket *tw, char *tmpbuf, int i) ...@@ -2219,137 +2509,89 @@ static void get_timewait_sock(struct tcp_tw_bucket *tw, char *tmpbuf, int i)
#define TMPSZ 150 #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; struct tcp_iter_state* st;
off_t begin, pos = 0;
char tmpbuf[TMPSZ + 1]; char tmpbuf[TMPSZ + 1];
if (offset < TMPSZ) if (v == (void *)1) {
len += sprintf(buffer, "%-*s\n", TMPSZ - 1, seq_printf(seq, "%-*s\n", TMPSZ - 1,
" sl local_address rem_address st tx_queue " " sl local_address rem_address st tx_queue "
"rx_queue tr tm->when retrnsmt uid timeout " "rx_queue tr tm->when retrnsmt uid timeout "
"inode"); "inode");
goto out;
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;
}
}
}
}
read_unlock_bh(&tp->syn_wait_lock);
/* Completed requests are in normal socket hash table */
}
} }
tcp_listen_unlock(); st = seq->private;
local_bh_disable(); 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;
}
seq_printf(seq, "%-*s\n", TMPSZ - 1, tmpbuf);
out:
return 0;
}
/* Next, walk established hash chain. */ static struct seq_operations tcp_seq_ops = {
for (i = 0; i < tcp_ehash_size; i++) { .start = tcp_seq_start,
struct tcp_ehash_bucket *head = &tcp_ehash[i]; .next = tcp_seq_next,
struct sock *sk; .stop = tcp_seq_stop,
struct tcp_tw_bucket *tw; .show = tcp_seq_show,
};
read_lock(&head->lock); static int tcp_seq_open(struct inode *inode, struct file *file)
for (sk = head->chain; sk; sk = sk->next, num++) { {
if (!TCP_INET_FAMILY(sk->family)) struct seq_file *seq;
continue; int rc = -ENOMEM;
pos += TMPSZ; struct tcp_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
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);
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);
goto out;
}
}
read_unlock(&head->lock);
}
if (!s)
goto out;
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: out:
local_bh_enable(); return rc;
out_no_bh: out_kfree:
kfree(s);
goto out;
}
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); int __init tcp_proc_init(void)
*start = buffer + begin; {
len -= begin; int rc = 0;
if (len > length) struct proc_dir_entry *p = create_proc_entry("tcp", S_IRUGO, proc_net);
len = length;
if (len < 0) if (p)
len = 0; p->proc_fops = &tcp_seq_fops;
return len; 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 = { struct proto tcp_prot = {
.name = "TCP", .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