9a32144e9d
Many struct file_operations in the kernel can be "const". Marking them const moves these to the .rodata section, which avoids false sharing with potential dirty data. In addition it'll catch accidental writes at compile time to these shared resources. Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
407 lines
9.1 KiB
C
407 lines
9.1 KiB
C
/*
|
|
* IPX proc routines
|
|
*
|
|
* Copyright(C) Arnaldo Carvalho de Melo <acme@conectiva.com.br>, 2002
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#ifdef CONFIG_PROC_FS
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/seq_file.h>
|
|
#include <net/tcp_states.h>
|
|
#include <net/ipx.h>
|
|
|
|
static __inline__ struct ipx_interface *ipx_get_interface_idx(loff_t pos)
|
|
{
|
|
struct ipx_interface *i;
|
|
|
|
list_for_each_entry(i, &ipx_interfaces, node)
|
|
if (!pos--)
|
|
goto out;
|
|
i = NULL;
|
|
out:
|
|
return i;
|
|
}
|
|
|
|
static struct ipx_interface *ipx_interfaces_next(struct ipx_interface *i)
|
|
{
|
|
struct ipx_interface *rc = NULL;
|
|
|
|
if (i->node.next != &ipx_interfaces)
|
|
rc = list_entry(i->node.next, struct ipx_interface, node);
|
|
return rc;
|
|
}
|
|
|
|
static void *ipx_seq_interface_start(struct seq_file *seq, loff_t *pos)
|
|
{
|
|
loff_t l = *pos;
|
|
|
|
spin_lock_bh(&ipx_interfaces_lock);
|
|
return l ? ipx_get_interface_idx(--l) : SEQ_START_TOKEN;
|
|
}
|
|
|
|
static void *ipx_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
{
|
|
struct ipx_interface *i;
|
|
|
|
++*pos;
|
|
if (v == SEQ_START_TOKEN)
|
|
i = ipx_interfaces_head();
|
|
else
|
|
i = ipx_interfaces_next(v);
|
|
return i;
|
|
}
|
|
|
|
static void ipx_seq_interface_stop(struct seq_file *seq, void *v)
|
|
{
|
|
spin_unlock_bh(&ipx_interfaces_lock);
|
|
}
|
|
|
|
static int ipx_seq_interface_show(struct seq_file *seq, void *v)
|
|
{
|
|
struct ipx_interface *i;
|
|
|
|
if (v == SEQ_START_TOKEN) {
|
|
seq_puts(seq, "Network Node_Address Primary Device "
|
|
"Frame_Type");
|
|
#ifdef IPX_REFCNT_DEBUG
|
|
seq_puts(seq, " refcnt");
|
|
#endif
|
|
seq_puts(seq, "\n");
|
|
goto out;
|
|
}
|
|
|
|
i = v;
|
|
seq_printf(seq, "%08lX ", (unsigned long int)ntohl(i->if_netnum));
|
|
seq_printf(seq, "%02X%02X%02X%02X%02X%02X ",
|
|
i->if_node[0], i->if_node[1], i->if_node[2],
|
|
i->if_node[3], i->if_node[4], i->if_node[5]);
|
|
seq_printf(seq, "%-9s", i == ipx_primary_net ? "Yes" : "No");
|
|
seq_printf(seq, "%-11s", ipx_device_name(i));
|
|
seq_printf(seq, "%-9s", ipx_frame_name(i->if_dlink_type));
|
|
#ifdef IPX_REFCNT_DEBUG
|
|
seq_printf(seq, "%6d", atomic_read(&i->refcnt));
|
|
#endif
|
|
seq_puts(seq, "\n");
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
static struct ipx_route *ipx_routes_head(void)
|
|
{
|
|
struct ipx_route *rc = NULL;
|
|
|
|
if (!list_empty(&ipx_routes))
|
|
rc = list_entry(ipx_routes.next, struct ipx_route, node);
|
|
return rc;
|
|
}
|
|
|
|
static struct ipx_route *ipx_routes_next(struct ipx_route *r)
|
|
{
|
|
struct ipx_route *rc = NULL;
|
|
|
|
if (r->node.next != &ipx_routes)
|
|
rc = list_entry(r->node.next, struct ipx_route, node);
|
|
return rc;
|
|
}
|
|
|
|
static __inline__ struct ipx_route *ipx_get_route_idx(loff_t pos)
|
|
{
|
|
struct ipx_route *r;
|
|
|
|
list_for_each_entry(r, &ipx_routes, node)
|
|
if (!pos--)
|
|
goto out;
|
|
r = NULL;
|
|
out:
|
|
return r;
|
|
}
|
|
|
|
static void *ipx_seq_route_start(struct seq_file *seq, loff_t *pos)
|
|
{
|
|
loff_t l = *pos;
|
|
read_lock_bh(&ipx_routes_lock);
|
|
return l ? ipx_get_route_idx(--l) : SEQ_START_TOKEN;
|
|
}
|
|
|
|
static void *ipx_seq_route_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
{
|
|
struct ipx_route *r;
|
|
|
|
++*pos;
|
|
if (v == SEQ_START_TOKEN)
|
|
r = ipx_routes_head();
|
|
else
|
|
r = ipx_routes_next(v);
|
|
return r;
|
|
}
|
|
|
|
static void ipx_seq_route_stop(struct seq_file *seq, void *v)
|
|
{
|
|
read_unlock_bh(&ipx_routes_lock);
|
|
}
|
|
|
|
static int ipx_seq_route_show(struct seq_file *seq, void *v)
|
|
{
|
|
struct ipx_route *rt;
|
|
|
|
if (v == SEQ_START_TOKEN) {
|
|
seq_puts(seq, "Network Router_Net Router_Node\n");
|
|
goto out;
|
|
}
|
|
rt = v;
|
|
seq_printf(seq, "%08lX ", (unsigned long int)ntohl(rt->ir_net));
|
|
if (rt->ir_routed)
|
|
seq_printf(seq, "%08lX %02X%02X%02X%02X%02X%02X\n",
|
|
(long unsigned int)ntohl(rt->ir_intrfc->if_netnum),
|
|
rt->ir_router_node[0], rt->ir_router_node[1],
|
|
rt->ir_router_node[2], rt->ir_router_node[3],
|
|
rt->ir_router_node[4], rt->ir_router_node[5]);
|
|
else
|
|
seq_puts(seq, "Directly Connected\n");
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
static __inline__ struct sock *ipx_get_socket_idx(loff_t pos)
|
|
{
|
|
struct sock *s = NULL;
|
|
struct hlist_node *node;
|
|
struct ipx_interface *i;
|
|
|
|
list_for_each_entry(i, &ipx_interfaces, node) {
|
|
spin_lock_bh(&i->if_sklist_lock);
|
|
sk_for_each(s, node, &i->if_sklist) {
|
|
if (!pos)
|
|
break;
|
|
--pos;
|
|
}
|
|
spin_unlock_bh(&i->if_sklist_lock);
|
|
if (!pos) {
|
|
if (node)
|
|
goto found;
|
|
break;
|
|
}
|
|
}
|
|
s = NULL;
|
|
found:
|
|
return s;
|
|
}
|
|
|
|
static void *ipx_seq_socket_start(struct seq_file *seq, loff_t *pos)
|
|
{
|
|
loff_t l = *pos;
|
|
|
|
spin_lock_bh(&ipx_interfaces_lock);
|
|
return l ? ipx_get_socket_idx(--l) : SEQ_START_TOKEN;
|
|
}
|
|
|
|
static void *ipx_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
{
|
|
struct sock* sk, *next;
|
|
struct ipx_interface *i;
|
|
struct ipx_sock *ipxs;
|
|
|
|
++*pos;
|
|
if (v == SEQ_START_TOKEN) {
|
|
sk = NULL;
|
|
i = ipx_interfaces_head();
|
|
if (!i)
|
|
goto out;
|
|
sk = sk_head(&i->if_sklist);
|
|
if (sk)
|
|
spin_lock_bh(&i->if_sklist_lock);
|
|
goto out;
|
|
}
|
|
sk = v;
|
|
next = sk_next(sk);
|
|
if (next) {
|
|
sk = next;
|
|
goto out;
|
|
}
|
|
ipxs = ipx_sk(sk);
|
|
i = ipxs->intrfc;
|
|
spin_unlock_bh(&i->if_sklist_lock);
|
|
sk = NULL;
|
|
for (;;) {
|
|
i = ipx_interfaces_next(i);
|
|
if (!i)
|
|
break;
|
|
spin_lock_bh(&i->if_sklist_lock);
|
|
if (!hlist_empty(&i->if_sklist)) {
|
|
sk = sk_head(&i->if_sklist);
|
|
break;
|
|
}
|
|
spin_unlock_bh(&i->if_sklist_lock);
|
|
}
|
|
out:
|
|
return sk;
|
|
}
|
|
|
|
static int ipx_seq_socket_show(struct seq_file *seq, void *v)
|
|
{
|
|
struct sock *s;
|
|
struct ipx_sock *ipxs;
|
|
|
|
if (v == SEQ_START_TOKEN) {
|
|
#ifdef CONFIG_IPX_INTERN
|
|
seq_puts(seq, "Local_Address "
|
|
"Remote_Address Tx_Queue "
|
|
"Rx_Queue State Uid\n");
|
|
#else
|
|
seq_puts(seq, "Local_Address Remote_Address "
|
|
"Tx_Queue Rx_Queue State Uid\n");
|
|
#endif
|
|
goto out;
|
|
}
|
|
|
|
s = v;
|
|
ipxs = ipx_sk(s);
|
|
#ifdef CONFIG_IPX_INTERN
|
|
seq_printf(seq, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ",
|
|
(unsigned long)ntohl(ipxs->intrfc->if_netnum),
|
|
ipxs->node[0], ipxs->node[1], ipxs->node[2], ipxs->node[3],
|
|
ipxs->node[4], ipxs->node[5], ntohs(ipxs->port));
|
|
#else
|
|
seq_printf(seq, "%08lX:%04X ", (unsigned long) ntohl(ipxs->intrfc->if_netnum),
|
|
ntohs(ipxs->port));
|
|
#endif /* CONFIG_IPX_INTERN */
|
|
if (s->sk_state != TCP_ESTABLISHED)
|
|
seq_printf(seq, "%-28s", "Not_Connected");
|
|
else {
|
|
seq_printf(seq, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ",
|
|
(unsigned long)ntohl(ipxs->dest_addr.net),
|
|
ipxs->dest_addr.node[0], ipxs->dest_addr.node[1],
|
|
ipxs->dest_addr.node[2], ipxs->dest_addr.node[3],
|
|
ipxs->dest_addr.node[4], ipxs->dest_addr.node[5],
|
|
ntohs(ipxs->dest_addr.sock));
|
|
}
|
|
|
|
seq_printf(seq, "%08X %08X %02X %03d\n",
|
|
atomic_read(&s->sk_wmem_alloc),
|
|
atomic_read(&s->sk_rmem_alloc),
|
|
s->sk_state, SOCK_INODE(s->sk_socket)->i_uid);
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
static struct seq_operations ipx_seq_interface_ops = {
|
|
.start = ipx_seq_interface_start,
|
|
.next = ipx_seq_interface_next,
|
|
.stop = ipx_seq_interface_stop,
|
|
.show = ipx_seq_interface_show,
|
|
};
|
|
|
|
static struct seq_operations ipx_seq_route_ops = {
|
|
.start = ipx_seq_route_start,
|
|
.next = ipx_seq_route_next,
|
|
.stop = ipx_seq_route_stop,
|
|
.show = ipx_seq_route_show,
|
|
};
|
|
|
|
static struct seq_operations ipx_seq_socket_ops = {
|
|
.start = ipx_seq_socket_start,
|
|
.next = ipx_seq_socket_next,
|
|
.stop = ipx_seq_interface_stop,
|
|
.show = ipx_seq_socket_show,
|
|
};
|
|
|
|
static int ipx_seq_route_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &ipx_seq_route_ops);
|
|
}
|
|
|
|
static int ipx_seq_interface_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &ipx_seq_interface_ops);
|
|
}
|
|
|
|
static int ipx_seq_socket_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &ipx_seq_socket_ops);
|
|
}
|
|
|
|
static const struct file_operations ipx_seq_interface_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = ipx_seq_interface_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release,
|
|
};
|
|
|
|
static const struct file_operations ipx_seq_route_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = ipx_seq_route_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release,
|
|
};
|
|
|
|
static const struct file_operations ipx_seq_socket_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = ipx_seq_socket_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release,
|
|
};
|
|
|
|
static struct proc_dir_entry *ipx_proc_dir;
|
|
|
|
int __init ipx_proc_init(void)
|
|
{
|
|
struct proc_dir_entry *p;
|
|
int rc = -ENOMEM;
|
|
|
|
ipx_proc_dir = proc_mkdir("ipx", proc_net);
|
|
|
|
if (!ipx_proc_dir)
|
|
goto out;
|
|
p = create_proc_entry("interface", S_IRUGO, ipx_proc_dir);
|
|
if (!p)
|
|
goto out_interface;
|
|
|
|
p->proc_fops = &ipx_seq_interface_fops;
|
|
p = create_proc_entry("route", S_IRUGO, ipx_proc_dir);
|
|
if (!p)
|
|
goto out_route;
|
|
|
|
p->proc_fops = &ipx_seq_route_fops;
|
|
p = create_proc_entry("socket", S_IRUGO, ipx_proc_dir);
|
|
if (!p)
|
|
goto out_socket;
|
|
|
|
p->proc_fops = &ipx_seq_socket_fops;
|
|
|
|
rc = 0;
|
|
out:
|
|
return rc;
|
|
out_socket:
|
|
remove_proc_entry("route", ipx_proc_dir);
|
|
out_route:
|
|
remove_proc_entry("interface", ipx_proc_dir);
|
|
out_interface:
|
|
remove_proc_entry("ipx", proc_net);
|
|
goto out;
|
|
}
|
|
|
|
void __exit ipx_proc_exit(void)
|
|
{
|
|
remove_proc_entry("interface", ipx_proc_dir);
|
|
remove_proc_entry("route", ipx_proc_dir);
|
|
remove_proc_entry("socket", ipx_proc_dir);
|
|
remove_proc_entry("ipx", proc_net);
|
|
}
|
|
|
|
#else /* CONFIG_PROC_FS */
|
|
|
|
int __init ipx_proc_init(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void __exit ipx_proc_exit(void)
|
|
{
|
|
}
|
|
|
|
#endif /* CONFIG_PROC_FS */
|