changeset 0:a9a56b9dd8a6 default tip

Import 64network & watch64
author Josef "Jeff" Sipek <jeffpc@josefsipek.net>
date Thu, 23 Aug 2007 20:48:10 -0400
parents
children
files 64network watch64
diffstat 2 files changed, 895 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/64network	Thu Aug 23 20:48:10 2007 -0400
@@ -0,0 +1,351 @@
+diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
+index f671cd2..339c479 100644
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -14,6 +14,7 @@
+  *		Alan Cox, <Alan.Cox@linux.org>
+  *		Bjorn Ekwall. <bj0rn@blox.se>
+  *              Pekka Riikonen <priikone@poseidon.pspt.fi>
++ *		Josef "Jeff" Sipek <jeffpc@josefsipek.net>
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+@@ -1082,6 +1083,10 @@ static inline int skb_bond_should_drop(struct sk_buff *skb)
+ 	return 0;
+ }
+ 
++/* Register/unregister all the members of struct net_device_stats with watch64 */
++void		net_register_stats64(struct net_device_stats* stats);
++void		net_unregister_stats64(struct net_device_stats* stats);
++
+ #endif /* __KERNEL__ */
+ 
+ #endif	/* _LINUX_DEV_H */
+diff --git a/net/core/dev.c b/net/core/dev.c
+index f2b6111..4572118 100644
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -18,6 +18,7 @@
+  *		Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+  *		Adam Sulmicki <adam@cfar.umd.edu>
+  *              Pekka Riikonen <priikone@poesidon.pspt.fi>
++ *		Josef "Jeff" Sipek <jeffpc@josefsipek.net>
+  *
+  *	Changes:
+  *              D.J. Barrow     :       Fixed bug where dev->refcnt gets set
+@@ -70,6 +71,7 @@
+  *              			indefinitely on dev->refcnt
+  * 		J Hadi Salim	:	- Backlog queue sampling
+  *				        - netif_rx() feedback
++ *	Josef "Jeff" Sipek	:	Added watch64 calls for network statistics
+  */
+ 
+ #include <asm/uaccess.h>
+@@ -117,6 +119,7 @@
+ #include <linux/err.h>
+ #include <linux/ctype.h>
+ #include <linux/if_arp.h>
++#include <linux/watch64.h>
+ 
+ /*
+  *	The list of packet types we will receive (as opposed to discard)
+@@ -2203,6 +2206,49 @@ static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
+ 		   stats->tx_compressed);
+ }
+ 
++static void dev_seq_printf_stats64(struct seq_file *seq, struct net_device *dev)
++{
++	if (dev->get_stats) {
++		struct net_device_stats *stats = dev->get_stats(dev);
++
++		seq_printf(seq, "%6s:%8llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
++				"%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
++			   dev->name, watch64_getval(&stats->rx_bytes,NULL),
++			   watch64_getval(&stats->rx_packets,NULL),
++			   watch64_getval(&stats->rx_errors,NULL),
++			   watch64_getval(&stats->rx_dropped,NULL) +
++			     watch64_getval(&stats->rx_missed_errors,NULL),
++			   watch64_getval(&stats->rx_fifo_errors,NULL),
++			   watch64_getval(&stats->rx_length_errors,NULL) + 
++			     watch64_getval(&stats->rx_over_errors,NULL) +
++			     watch64_getval(&stats->rx_crc_errors,NULL) +
++			     watch64_getval(&stats->rx_frame_errors,NULL),
++			   watch64_getval(&stats->rx_compressed,NULL),
++			   watch64_getval(&stats->multicast,NULL),
++			   watch64_getval(&stats->tx_bytes,NULL),
++			   watch64_getval(&stats->tx_packets,NULL),
++			   watch64_getval(&stats->tx_errors,NULL),
++			   watch64_getval(&stats->tx_dropped,NULL),
++			   watch64_getval(&stats->tx_fifo_errors,NULL),
++			   watch64_getval(&stats->collisions,NULL),
++			   watch64_getval(&stats->tx_carrier_errors,NULL) +
++			     watch64_getval(&stats->tx_aborted_errors,NULL) +
++			     watch64_getval(&stats->tx_window_errors,NULL) +
++			     watch64_getval(&stats->tx_heartbeat_errors,NULL),
++			   watch64_getval(&stats->tx_compressed,NULL));
++	} else
++		seq_printf(seq, "%6s: No statistics available.\n", dev->name);
++}
++
++static void dev_seq_show_header(struct seq_file *seq)
++{
++	seq_puts(seq, "Inter-|   Receive                            "
++		      "                    |  Transmit\n"
++		      " face |bytes    packets errs drop fifo frame "
++		      "compressed multicast|bytes    packets errs "
++		      "drop fifo colls carrier compressed\n");
++}
++
+ /*
+  *	Called from the PROCfs module. This now uses the new arbitrary sized
+  *	/proc/net interface to create /proc/net/dev
+@@ -2210,16 +2256,21 @@ static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
+ static int dev_seq_show(struct seq_file *seq, void *v)
+ {
+ 	if (v == SEQ_START_TOKEN)
+-		seq_puts(seq, "Inter-|   Receive                            "
+-			      "                    |  Transmit\n"
+-			      " face |bytes    packets errs drop fifo frame "
+-			      "compressed multicast|bytes    packets errs "
+-			      "drop fifo colls carrier compressed\n");
++		dev_seq_show_header(seq);
+ 	else
+ 		dev_seq_printf_stats(seq, v);
+ 	return 0;
+ }
+ 
++static int dev_seq_show64(struct seq_file *seq, void *v)
++{
++	if (v == SEQ_START_TOKEN)
++		dev_seq_show_header(seq);
++	else
++		dev_seq_printf_stats64(seq, v);
++	return 0;
++}
++
+ static struct netif_rx_stats *softnet_get_online(loff_t *pos)
+ {
+ 	struct netif_rx_stats *rc = NULL;
+@@ -2266,11 +2317,23 @@ static const struct seq_operations dev_seq_ops = {
+ 	.show  = dev_seq_show,
+ };
+ 
++static const struct seq_operations dev_seq_ops64 = {
++	.start = dev_seq_start,
++	.next  = dev_seq_next,
++	.stop  = dev_seq_stop,
++	.show  = dev_seq_show64,
++};
++
+ static int dev_seq_open(struct inode *inode, struct file *file)
+ {
+ 	return seq_open(file, &dev_seq_ops);
+ }
+ 
++static int dev_seq_open64(struct inode *inode, struct file *file)
++{
++	return seq_open(file, &dev_seq_ops64);
++}
++
+ static const struct file_operations dev_seq_fops = {
+ 	.owner	 = THIS_MODULE,
+ 	.open    = dev_seq_open,
+@@ -2279,6 +2342,14 @@ static const struct file_operations dev_seq_fops = {
+ 	.release = seq_release,
+ };
+ 
++static struct file_operations dev_seq_fops64 = {
++	.owner	 = THIS_MODULE,
++	.open    = dev_seq_open64,
++	.read    = seq_read,
++	.llseek  = seq_lseek,
++	.release = seq_release,
++};
++
+ static const struct seq_operations softnet_seq_ops = {
+ 	.start = softnet_seq_start,
+ 	.next  = softnet_seq_next,
+@@ -2434,10 +2505,12 @@ static int __init dev_proc_init(void)
+ 
+ 	if (!proc_net_fops_create("dev", S_IRUGO, &dev_seq_fops))
+ 		goto out;
+-	if (!proc_net_fops_create("softnet_stat", S_IRUGO, &softnet_seq_fops))
++	if (!proc_net_fops_create("dev64", S_IRUGO, &dev_seq_fops64))
+ 		goto out_dev;
+-	if (!proc_net_fops_create("ptype", S_IRUGO, &ptype_seq_fops))
++	if (!proc_net_fops_create("softnet_stat", S_IRUGO, &softnet_seq_fops))
+ 		goto out_dev2;
++	if (!proc_net_fops_create("ptype", S_IRUGO, &ptype_seq_fops))
++		goto out_dev3;
+ 
+ 	if (wext_proc_init())
+ 		goto out_softnet;
+@@ -2446,8 +2519,10 @@ out:
+ 	return rc;
+ out_softnet:
+ 	proc_net_remove("ptype");
+-out_dev2:
++out_dev3:
+ 	proc_net_remove("softnet_stat");
++out_dev2:
++	proc_net_remove("dev64");
+ out_dev:
+ 	proc_net_remove("dev");
+ 	goto out;
+@@ -3004,6 +3079,98 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
+ 	}
+ }
+ 
++/*
++ *	Register all the members of the net_device_stats structure
++ *
++ */
++ 
++void net_register_stats64(struct net_device_stats* stats)
++{
++	if (!stats)
++		return;
++
++	watch64_register(&stats->tx_packets,0);
++	watch64_enable  (&stats->tx_packets,NULL);
++	watch64_register(&stats->rx_packets,0);
++	watch64_enable  (&stats->rx_packets,NULL);
++	watch64_register(&stats->tx_bytes,0);
++	watch64_enable  (&stats->tx_bytes,NULL);
++	watch64_register(&stats->rx_bytes,0);
++	watch64_enable  (&stats->rx_bytes,NULL);
++	watch64_register(&stats->tx_errors,0);
++	watch64_enable  (&stats->tx_errors,NULL);
++	watch64_register(&stats->rx_errors,0);
++	watch64_enable  (&stats->rx_errors,NULL);
++	watch64_register(&stats->tx_dropped,0);
++	watch64_enable  (&stats->tx_dropped,NULL);
++	watch64_register(&stats->rx_dropped,0);
++	watch64_enable  (&stats->rx_dropped,NULL);
++	watch64_register(&stats->multicast,0);
++	watch64_enable  (&stats->multicast,NULL);
++	watch64_register(&stats->collisions,0);
++	watch64_enable  (&stats->collisions,NULL);
++	watch64_register(&stats->rx_length_errors,0);
++	watch64_enable  (&stats->rx_length_errors,NULL);
++	watch64_register(&stats->rx_over_errors,0);
++	watch64_enable  (&stats->rx_over_errors,NULL);
++	watch64_register(&stats->rx_crc_errors,0);
++	watch64_enable  (&stats->rx_crc_errors,NULL);
++	watch64_register(&stats->rx_frame_errors,0);
++	watch64_enable  (&stats->rx_frame_errors,NULL);
++	watch64_register(&stats->rx_fifo_errors,0);
++	watch64_enable  (&stats->rx_fifo_errors,NULL);
++	watch64_register(&stats->rx_missed_errors,0);
++	watch64_enable  (&stats->rx_missed_errors,NULL);
++	watch64_register(&stats->tx_aborted_errors,0);
++	watch64_enable  (&stats->tx_aborted_errors,NULL);
++	watch64_register(&stats->tx_carrier_errors,0);
++	watch64_enable  (&stats->tx_carrier_errors,NULL);
++	watch64_register(&stats->tx_fifo_errors,0);
++	watch64_enable  (&stats->tx_fifo_errors,NULL);
++	watch64_register(&stats->tx_heartbeat_errors,0);
++	watch64_enable  (&stats->tx_heartbeat_errors,NULL);
++	watch64_register(&stats->tx_window_errors,0);
++	watch64_enable  (&stats->tx_window_errors,NULL);
++	watch64_register(&stats->rx_compressed,0);
++	watch64_enable  (&stats->rx_compressed,NULL);
++	watch64_register(&stats->tx_compressed,0);
++	watch64_enable  (&stats->tx_compressed,NULL);
++}
++
++/*
++ *	Unregister all the members of the net_device_stats structure
++ *
++ */
++ 
++void net_unregister_stats64(struct net_device_stats* stats)
++{
++	if (!stats)
++		return;
++
++	watch64_unregister(&stats->tx_packets,0);
++	watch64_unregister(&stats->rx_packets,0);
++	watch64_unregister(&stats->tx_bytes,0);
++	watch64_unregister(&stats->rx_bytes,0);
++	watch64_unregister(&stats->tx_errors,0);
++	watch64_unregister(&stats->rx_errors,0);
++	watch64_unregister(&stats->tx_dropped,0);
++	watch64_unregister(&stats->rx_dropped,0);
++	watch64_unregister(&stats->multicast,0);
++	watch64_unregister(&stats->collisions,0);
++	watch64_unregister(&stats->rx_length_errors,0);
++	watch64_unregister(&stats->rx_over_errors,0);
++	watch64_unregister(&stats->rx_crc_errors,0);
++	watch64_unregister(&stats->rx_frame_errors,0);
++	watch64_unregister(&stats->rx_fifo_errors,0);
++	watch64_unregister(&stats->rx_missed_errors,0);
++	watch64_unregister(&stats->tx_aborted_errors,0);
++	watch64_unregister(&stats->tx_carrier_errors,0);
++	watch64_unregister(&stats->tx_fifo_errors,0);
++	watch64_unregister(&stats->tx_heartbeat_errors,0);
++	watch64_unregister(&stats->tx_window_errors,0);
++	watch64_unregister(&stats->rx_compressed,0);
++	watch64_unregister(&stats->tx_compressed,0);
++}
+ 
+ /**
+  *	dev_new_index	-	allocate an ifindex
+@@ -3153,6 +3320,9 @@ int register_netdevice(struct net_device *dev)
+ 	 *	device is present.
+ 	 */
+ 
++	if (dev->get_stats)
++		net_register_stats64(dev->get_stats(dev));
++	
+ 	set_bit(__LINK_STATE_PRESENT, &dev->state);
+ 
+ 	dev_init_scheduler(dev);
+@@ -3162,7 +3332,7 @@ int register_netdevice(struct net_device *dev)
+ 	hlist_add_head(&dev->index_hlist, dev_index_hash(dev->ifindex));
+ 	dev_hold(dev);
+ 	write_unlock_bh(&dev_base_lock);
+-
++	
+ 	/* Notify protocols, that a new device appeared. */
+ 	raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
+ 
+@@ -3448,6 +3618,9 @@ void unregister_netdevice(struct net_device *dev)
+ 	/* If device is running, close it first. */
+ 	if (dev->flags & IFF_UP)
+ 		dev_close(dev);
++		
++	if (dev->get_stats)
++		net_unregister_stats64(dev->get_stats(dev));
+ 
+ 	/* And unlink it from device chain. */
+ 	write_lock_bh(&dev_base_lock);
+diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
+index b21307b..8f7bf15 100644
+--- a/net/core/net-sysfs.c
++++ b/net/core/net-sysfs.c
+@@ -17,11 +17,13 @@
+ #include <linux/rtnetlink.h>
+ #include <linux/wireless.h>
+ #include <net/iw_handler.h>
++#include <linux/watch64.h>
+ 
+ static const char fmt_hex[] = "%#x\n";
+ static const char fmt_long_hex[] = "%#lx\n";
+ static const char fmt_dec[] = "%d\n";
+ static const char fmt_ulong[] = "%lu\n";
++static const char fmt_ullong[] = "%llu\n";
+ 
+ static inline int dev_isalive(const struct net_device *dev)
+ {
+@@ -266,8 +268,8 @@ static ssize_t netstat_show(const struct device *d,
+ 	read_lock(&dev_base_lock);
+ 	if (dev_isalive(dev) && dev->get_stats &&
+ 	    (stats = (*dev->get_stats)(dev)))
+-		ret = sprintf(buf, fmt_ulong,
+-			      *(unsigned long *)(((u8 *) stats) + offset));
++		ret = sprintf(buf, fmt_ullong,
++				watch64_getval((unsigned long *)(((u8 *) stats) + offset), NULL));
+ 
+ 	read_unlock(&dev_base_lock);
+ 	return ret;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/watch64	Thu Aug 23 20:48:10 2007 -0400
@@ -0,0 +1,544 @@
+diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX
+index f08ca95..f554bd2 100644
+--- a/Documentation/00-INDEX
++++ b/Documentation/00-INDEX
+@@ -292,6 +292,8 @@ vm/
+ 	- directory with info on the Linux vm code.
+ voyager.txt
+ 	- guide to running Linux on the Voyager architecture.
++watch64.txt
++	- watch64 API description
+ watchdog/
+ 	- how to auto-reboot Linux if it has "fallen and can't get up". ;-)
+ x86_64/
+diff --git a/Documentation/watch64.txt b/Documentation/watch64.txt
+new file mode 100644
+index 0000000..bedb736
+--- /dev/null
++++ b/Documentation/watch64.txt
+@@ -0,0 +1,35 @@
++int watch64_register(unsigned long* ptr, unsigned int interval);
++
++	- Registers *ptr to be monitored every interval jiffies.
++	- If interval==0, WATCH64_INTERVAL will be used (HZ/10 by default)
++
++int watch64_unregister(unsigned long* ptr, struct watch64* st);
++
++	- Unregister *ptr
++	- st is optional pointer to the struct containing the registration
++		information
++	- if st==NULL, it will be looked up automatically
++
++struct watch64* watch64_find(unsigned long* ptr);
++
++	- Return struct with registration information of *ptr
++
++int watch64_disable(unsigned long* ptr, struct watch64* st);
++
++	- Disable *ptr from being monitored, without removing it from the list
++	- st is optional (see watch64_unregister for more information)
++
++int watch64_enable(unsigned long* ptr, struct watch64* st);
++
++	- Enable *ptr from being monitored (opposite of watch64_disable)
++	- st is optional (see watch64_unregister for more information)
++
++int watch64_toggle(unsigned long* ptr, struct watch64* st);
++
++	- Toggle the enable/disable status
++	- st is optional (see watch64_unregister for more information)
++
++inline u_int64_t watch64_getval(unsigned long* ptr, struct watch64* st);
++
++	- Return the whole 64-bit counter
++	- st is optional (see watch64_unregister for more information)
+diff --git a/include/linux/watch64.h b/include/linux/watch64.h
+new file mode 100644
+index 0000000..d8d7777
+--- /dev/null
++++ b/include/linux/watch64.h
+@@ -0,0 +1,174 @@
++/*
++ *  inclue/linux/watch64.h
++ *
++ *  Copyright (C) 2003-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#ifndef _LINUX_64WATCH_H
++#define _LINUX_64WATCH_H
++
++#include <linux/list.h>
++#include <linux/timer.h>
++#include <linux/kernel.h>
++#include <linux/seqlock.h>
++#include <linux/rcupdate.h>
++
++#define WATCH64_INTERVAL	(HZ/10)
++#define WATCH64_MINIMUM		(HZ/20)
++#define WATCH64_MAGIC		0x573634
++
++extern struct list_head watch64_head;
++
++#if (BITS_PER_LONG == 64)
++
++struct watch64 {
++};
++
++#else
++
++struct watch64 {
++	struct list_head list;
++	unsigned long *ptr;
++	unsigned long oldval;
++	u_int64_t total;
++	unsigned int interval;
++	int active;
++	seqlock_t lock;
++	struct rcu_head rcuhead;
++};
++
++#endif /* (BITS_PER_LONG == 64) */
++
++/*
++ *   Prototypes
++ */
++
++void watch64_init(void);
++void watch64_run(unsigned long var);
++int watch64_register(unsigned long* ptr, unsigned int interval);
++int watch64_unregister(unsigned long* ptr, struct watch64* st);
++void watch64_rcufree(struct rcu_head* p);
++struct watch64* watch64_find(unsigned long* ptr);
++int watch64_disable(unsigned long* ptr, struct watch64* st);
++int watch64_enable(unsigned long* ptr, struct watch64* st);
++int watch64_toggle(unsigned long* ptr, struct watch64* st);
++
++/*
++ * Inline
++ */
++
++#if (BITS_PER_LONG == 64)
++
++static inline struct watch64* __watch64_find(unsigned long* ptr)
++{
++	return NULL;
++}
++
++static inline int __watch64_enable(unsigned long* ptr, struct watch64* st)
++{
++	return 0;
++}
++
++static inline int __watch64_disable(unsigned long* ptr, struct watch64* st)
++{
++	return 0;
++}
++
++static inline u_int64_t watch64_getval(unsigned long* ptr, struct watch64* st)
++{
++	return (u_int64_t) *ptr;
++}
++
++#else
++
++/*
++ *   Find watch64 structure without RCU lock
++ */
++
++static inline struct watch64* __watch64_find(unsigned long* ptr)
++{
++	struct list_head* tmp;
++	struct watch64* watch64_struct;
++
++	list_for_each_rcu(tmp, &watch64_head) {
++		watch64_struct = list_entry(tmp, struct watch64, list);
++		if (watch64_struct->ptr==ptr)
++			return watch64_struct;
++	}
++
++	return NULL;
++}
++
++/*
++ *   Enable a variable watch without RCU lock
++ */
++
++static inline int __watch64_enable(unsigned long* ptr, struct watch64* st)
++{
++	if (!st)
++		st = __watch64_find(ptr);
++
++	if (!st)
++		return -EINVAL;
++
++	st->oldval = *ptr;
++	write_seqlock(&st->lock);
++	st->total  = (u_int64_t) st->oldval;
++	write_sequnlock(&st->lock);
++	st->active = 1;
++
++	return 0;
++}
++
++/*
++ *   Disable a variable watch without RCU lock
++ */
++
++static inline int __watch64_disable(unsigned long* ptr, struct watch64* st)
++{
++	if (!st)
++		st = watch64_find(ptr);
++
++	if (!st)
++		return -EINVAL;
++
++	st->active = 0;
++
++	return 0;
++}
++
++/*
++ *   Return the total 64-bit value
++ */
++
++static inline u_int64_t watch64_getval(unsigned long* ptr, struct watch64* st)
++{
++	unsigned int seq;
++	u_int64_t total;
++
++	rcu_read_lock();
++	if (!st)
++		st = __watch64_find(ptr);
++
++	if (!st) {
++		rcu_read_unlock();
++		return *ptr;
++	}
++
++	do {
++		seq = read_seqbegin(&st->lock);
++		total = st->total;
++	} while (read_seqretry(&st->lock, seq));
++	rcu_read_unlock();
++	
++	return total;
++}
++
++#endif /* (BITS_PER_LONG == 64) */
++
++#endif /* _LINUX_WATCH64_H */
+diff --git a/kernel/Makefile b/kernel/Makefile
+index 642d427..2f7c887 100644
+--- a/kernel/Makefile
++++ b/kernel/Makefile
+@@ -8,7 +8,8 @@ obj-y     = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
+ 	    signal.o sys.o kmod.o workqueue.o pid.o \
+ 	    rcupdate.o extable.o params.o posix-timers.o \
+ 	    kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
+-	    hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o
++	    hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \
++	    watch64.o
+ 
+ obj-$(CONFIG_STACKTRACE) += stacktrace.o
+ obj-y += time/
+diff --git a/kernel/watch64.c b/kernel/watch64.c
+new file mode 100644
+index 0000000..c8c1c74
+--- /dev/null
++++ b/kernel/watch64.c
+@@ -0,0 +1,290 @@
++/*
++ *  kernel/watch64.c
++ *
++ *  Copyright (C) 2003-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++ 
++#include <asm/param.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/timer.h>
++#include <linux/kernel.h>
++#include <linux/seqlock.h>
++#include <linux/spinlock.h>
++#include <linux/rcupdate.h>
++#include <linux/watch64.h>
++
++/*
++ *   Watch64 global variables
++ */
++
++spinlock_t watch64_biglock = SPIN_LOCK_UNLOCKED;
++LIST_HEAD(watch64_head);
++struct timer_list watch64_timer;
++int watch64_setup;
++
++#if (BITS_PER_LONG == 64)
++
++void watch64_init(void)
++{
++}
++
++void watch64_run(unsigned long var)
++{
++}
++
++int watch64_register(unsigned long* ptr, unsigned int interval)
++{
++	return 0;
++}
++
++int watch64_unregister(unsigned long* ptr, struct watch64* st)
++{
++	return 0;
++}
++
++void watch64_rcufree(struct rcu_head* p)
++{
++}
++
++struct watch64* watch64_find(unsigned long* ptr)
++{
++	return NULL;
++}
++
++int watch64_disable(unsigned long* ptr, struct watch64* st)
++{
++	return 0;
++}
++
++int watch64_enable(unsigned long* ptr, struct watch64* st)
++{
++	return 0;
++}
++
++int watch64_toggle(unsigned long* ptr, struct watch64* st)
++{
++	return 0;
++}
++
++
++#else
++
++/*
++ *   Initiate watch64 system
++ */
++
++void watch64_init(void)
++{
++	spin_lock(&watch64_biglock);
++	
++	if (watch64_setup==WATCH64_MAGIC) {
++		spin_unlock(&watch64_biglock);
++		return;
++	}
++	
++	printk(KERN_WARNING "watch64: 2003/08/22 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>\n");
++	printk(KERN_WARNING "watch64: Enabling Watch64 extensions...");
++
++	init_timer(&watch64_timer);
++	watch64_timer.function = watch64_run;
++	watch64_timer.data = (unsigned long) NULL;
++	watch64_timer.expires = jiffies + WATCH64_MINIMUM;
++	add_timer(&watch64_timer);
++
++	printk("done.\n");
++	
++	watch64_setup = WATCH64_MAGIC;
++	
++	spin_unlock(&watch64_biglock);
++}
++
++/*
++ *   Go through the list of registered variables and check them for changes
++ */
++
++void watch64_run(unsigned long var)
++{
++	struct list_head* entry;
++	struct watch64* watch_struct;
++	unsigned long tmp;
++
++	rcu_read_lock();
++	list_for_each_rcu(entry, &watch64_head) {
++		watch_struct = list_entry(entry, struct watch64, list);
++		if (*watch_struct->ptr != watch_struct->oldval) {
++			tmp = *watch_struct->ptr;
++			if (tmp > watch_struct->oldval) {
++				write_seqlock(&watch_struct->lock);
++				watch_struct->total += tmp - watch_struct->oldval;
++				write_sequnlock(&watch_struct->lock);
++			} else if (tmp < watch_struct->oldval) {
++				write_seqlock(&watch_struct->lock);
++				watch_struct->total += ((u_int64_t) 1<<BITS_PER_LONG) - watch_struct->oldval + tmp;
++				write_sequnlock(&watch_struct->lock);
++			}
++			watch_struct->oldval = tmp;
++		}
++	}
++	rcu_read_unlock();
++	
++	mod_timer(&watch64_timer, jiffies + WATCH64_MINIMUM);
++}
++
++/*
++ *   Register a new variable with watch64
++ */
++
++int watch64_register(unsigned long* ptr, unsigned int interval)
++{
++	struct watch64* temp;
++	
++	temp = (struct watch64*) kmalloc(sizeof(struct watch64),GFP_ATOMIC);
++
++	if (!temp)
++		return -ENOMEM;
++
++	if (watch64_setup!=WATCH64_MAGIC)
++		watch64_init();
++
++	temp->ptr = ptr;
++	temp->oldval = 0;
++	temp->total = 0;
++	if (interval==0)
++		temp->interval = WATCH64_INTERVAL;
++	else if (interval<WATCH64_MINIMUM) {
++		temp->interval = WATCH64_MINIMUM;
++		printk("watch64: attempted to add new watch with interval below %d jiffies",WATCH64_MINIMUM);
++	} else
++		temp->interval = interval;
++
++	temp->active = 0;
++	
++	seqlock_init(&temp->lock);
++
++	list_add_rcu(&temp->list, &watch64_head);
++
++	return 0;
++}
++
++/*
++ *   Unregister a variable with watch64
++ */
++
++int watch64_unregister(unsigned long* ptr, struct watch64* st)
++{
++	rcu_read_lock();
++	if (!st)
++		st = __watch64_find(ptr); 
++
++	if (!st)
++		return -EINVAL;
++
++	__watch64_disable(ptr, st);
++	list_del_rcu(&st->list);
++	
++	call_rcu(&st->rcuhead, watch64_rcufree);
++	rcu_read_unlock();
++
++	return 0;
++}
++
++/*
++ *   Free memory via RCU
++ */
++ 
++void watch64_rcufree(struct rcu_head* p)
++{
++	kfree(container_of(p, struct watch64, rcuhead));
++}
++
++/*
++ *   Find watch64 structure with RCU lock
++ */
++
++struct watch64* watch64_find(unsigned long* ptr)
++{
++	struct watch64* tmp;
++	
++	rcu_read_lock();
++	tmp = __watch64_find(ptr);
++	rcu_read_unlock();
++	
++	return tmp;
++}
++
++/*
++ *   Disable a variable watch with RCU lock
++ */
++
++int watch64_disable(unsigned long* ptr, struct watch64* st)
++{
++	int tmp;
++	
++	rcu_read_lock();
++	tmp = __watch64_disable(ptr,st);
++	rcu_read_unlock();
++	
++	return tmp;
++}
++ 
++/*
++ *   Enable a variable watch with RCU lock
++ */
++
++int watch64_enable(unsigned long* ptr, struct watch64* st)
++{
++	int tmp;
++	
++	rcu_read_lock();
++	tmp = __watch64_enable(ptr,st);
++	rcu_read_unlock();
++	
++	return tmp;
++}
++ 
++/*
++ *   Toggle a variable watch
++ */
++
++int watch64_toggle(unsigned long* ptr, struct watch64* st)
++{
++	rcu_read_lock();
++	if (!st)
++		st = __watch64_find(ptr);
++
++	if (!st) {
++		rcu_read_unlock();
++		return -EINVAL;
++	}
++
++	if (st->active)
++		__watch64_disable(ptr,st);
++	else
++		__watch64_enable(ptr,st);
++	rcu_read_unlock();
++
++	return 0;
++}
++
++#endif /* (BITS_PER_LONG == 64) */
++
++/*
++ *   Export all the necessary symbols
++ */
++
++EXPORT_SYMBOL(watch64_register);
++EXPORT_SYMBOL(watch64_unregister);
++EXPORT_SYMBOL(watch64_find);
++EXPORT_SYMBOL(watch64_disable);
++EXPORT_SYMBOL(watch64_enable);
++EXPORT_SYMBOL(watch64_toggle);
++