lkml.org 
[lkml]   [2004]   [Sep]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH 2.6] watch64: generic variable monitoring system
The watch64 system allows the programmer to specify the approximate interval
at which he wants his variables checked. If he tries to specify shorter
interval than the minimum a default value of HZ/10 is used. To minimize
locking, RCU and seqlock are used. On 64-bit systems, all is optimized away.

The following patch can be also pulled from http://jeffpc.bkbits.net/watch64-2.6

Josef "Jeff" Sipek.

Signed-off-by: Josef "Jeff" Sipek <jeffpc@optonline.net>


diff -Nru a/Documentation/00-INDEX b/Documentation/00-INDEX
--- a/Documentation/00-INDEX 2004-09-03 12:21:17 -04:00
+++ b/Documentation/00-INDEX 2004-09-03 12:21:17 -04:00
@@ -250,6 +250,8 @@
- directory with info regarding video/TV/radio cards and linux.
vm/
- directory with info on the Linux vm code.
+watch64.txt
+ - watch64 API description
watchdog/
- how to auto-reboot Linux if it has "fallen and can't get up". ;-)
x86_64/
diff -Nru a/Documentation/watch64.txt b/Documentation/watch64.txt
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/Documentation/watch64.txt 2004-09-03 12:21:17 -04:00
@@ -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 -Nru a/include/linux/watch64.h b/include/linux/watch64.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/include/linux/watch64.h 2004-09-03 12:21:17 -04:00
@@ -0,0 +1,63 @@
+/*
+ * inclue/linux/watch64.h
+ *
+ * Copyright (C) 2003 Josef "Jeff" Sipek <jeffpc@optonline.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
+
+#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);
+inline struct watch64* __watch64_find(unsigned long* ptr);
+int watch64_disable(unsigned long* ptr, struct watch64* st);
+inline int __watch64_disable(unsigned long* ptr, struct watch64* st);
+int watch64_enable(unsigned long* ptr, struct watch64* st);
+inline int __watch64_enable(unsigned long* ptr, struct watch64* st);
+int watch64_toggle(unsigned long* ptr, struct watch64* st);
+inline u_int64_t watch64_getval(unsigned long* ptr, struct watch64* st);
+
+#endif /* _LINUX_WATCH64_H */
diff -Nru a/kernel/Makefile b/kernel/Makefile
--- a/kernel/Makefile 2004-09-03 12:21:17 -04:00
+++ b/kernel/Makefile 2004-09-03 12:21:17 -04:00
@@ -7,7 +7,7 @@
sysctl.o capability.o ptrace.o timer.o user.o \
signal.o sys.o kmod.o workqueue.o pid.o \
rcupdate.o intermodule.o extable.o params.o posix-timers.o \
- kthread.o
+ kthread.o watch64.o

obj-$(CONFIG_FUTEX) += futex.o
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
diff -Nru a/kernel/watch64.c b/kernel/watch64.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/kernel/watch64.c 2004-09-03 12:21:17 -04:00
@@ -0,0 +1,392 @@
+/*
+ * kernel/watch64.c
+ *
+ * Copyright (C) 2003 Josef "Jeff" Sipek <jeffpc@optonline.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(void* p)
+{
+}
+
+struct watch64* watch64_find(unsigned long* ptr)
+{
+ return NULL;
+}
+
+struct watch64* __watch64_find(unsigned long* ptr)
+{
+ return NULL;
+}
+
+int watch64_disable(unsigned long* ptr, struct watch64* st)
+{
+ return 0;
+}
+
+int __watch64_disable(unsigned long* ptr, struct watch64* st)
+{
+ return 0;
+}
+
+int watch64_enable(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;
+}
+
+inline u_int64_t watch64_getval(unsigned long* ptr, struct watch64* st)
+{
+ return (u_int64_t) *ptr;
+}
+
+#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@optonline.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;
+}
+
+/*
+ * Find watch64 structure without RCU lock
+ */
+
+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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Disable a variable watch without RCU lock
+ */
+
+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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Enable a variable watch without RCU lock
+ */
+
+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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Return the total 64-bit value
+ */
+
+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) */
+
+/*
+ * 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);
+EXPORT_SYMBOL(watch64_getval);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
\
 
 \ /
  Last update: 2005-03-22 14:05    [from the cache]
©2003-2011 Jasper Spaans