fs/proc/proc_misc.c | 16 + include/linux/opahead.h | 175 +++++++++++ lib/Makefile | 2 lib/opahead.c | 736 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 928 insertions(+), 1 deletion(-) diff -puN /dev/null include/linux/opahead.h --- /dev/null 2004-04-06 17:27:52.000000000 +0400 +++ git-linux-nikita/include/linux/opahead.h 2005-11-23 18:44:27.000000000 +0300 @@ -0,0 +1,175 @@ +/* + * opahead.h - generic operation-ahead prediction engine + * + * Copyright (c) 2004, 2005 Nikita Danilov + * + * This file is released under the GPLv2. + * + */ + +/* + * This file is an interface to the op-ahead: a generic prediction engine. + * + * op-ahead implementation is in lib/opahead.c + * + * General description. + * + * op-ahead makes predictions in a certain domain consisting of events. Events + * happen sequentially. op-head clients notify engine about events that + * happen, and engine keeps track of event patterns. Engine then can be asked + * a question: "Given a sequence of N last events, what is the next event to + * happen most likely?" + * + * To answer this question, op-ahead keeps track of all sequences of N events + * ever observed in the domain. N is called domain depth, and said sequences + * are called paths. For each path op-ahead maintains a list of events that + * were observed to follow this path (that is, to happen immediately after + * events in the path happened in order). These following events (called + * guesses) are weighted: the weight is a guess is a number of times it was + * observed to follow this path. + * + * op-ahead operation is quite simple: when event E happens, op-ahead finds a + * path corresponding to last happened events, and increases the weight of + * guess corresponding to E (creating new guess if necessary). + * + * When asked to predict what event will follow given N events, op-ahead finds + * path corresponding to these events and returns the guess with the largest + * weight. If there are multiple guesses with the equal weight---random one is + * selected. + * + * When domain depth is 1, paths degenerate into events and op-ahead into + * modeling Markov chains. + * + * op-ahead algorithm is known as "Shannon Oracle". It is reported that in the + * domain of two events "tail" and "head", and with the depth of 5, Shannon + * Oracle robustly beats human player in the game of head and tails (that is, + * after some period of learning it's able to predict next move with the + * probability higher than 0.5). You are hereby requested to spend at least 10 + * percent of proceeds obtained from playing head and tails with the help of + * this kernel code to buy hardware and to provide it to Linux kernel + * developers. + * + * Usage. + * + * To use op-ahead client creates an instance of struct opa_domain with + * methods appropriate for its events. + * + * Predictions are made in the "context" which is an array of domain->depth + * pointers to events. This context is created by client (initialized by NULL + * pointers) and is passed to opa_{predict,happen}(). It is used to record + * latest events. When context is no longer needed, client calls + * opa_context_put(). + * + * When new event happens, client calls opa_happens() to record it. To predict + * future events opa_predict() is used. + * + * Related work. + * + * In http://www.usenix.org/events/usenix01/kroeger.html a similar (albeit + * more mathematically sophisticated) model is used to predict future file + * accesses and to drive inter-file read-ahead. + * + * TODO: + * + * Helper functions for reference-counted events. + * + */ + +#ifndef __OPAHEAD_H__ +#define __OPAHEAD_H__ + +#ifdef __KERNEL__ + +#include + +/* + * Event in a domain. op-ahead itself never looks inside the event. It's + * completely up to the client to define what events are as long as opa_domain + * methods (see below) are implemented. opa_event_t can be either pointer or + * scalar. + */ +typedef void *opa_event_t; + +struct seq_file; + +/* + * op-ahead domain. Client supplies this as argument to the + * opa_{predict,happens}(). + */ +struct opa_domain { + /* + * Called by op-ahead when it adds a reference to an event @ev, for + * example, when @ev is stored in a path, or used as a guess. This + * callback can be used to implement some form of reference counting + * if events are dynamically allocated, or can be a no-op, if events + * are not allocated (e.g., if they are scalars). + */ + void (*get) (opa_event_t ev); + /* + * Dual to the ->get(): called by op-ahead when reference to @ev is + * released. + */ + void (*put) (opa_event_t ev); + /* + * This is called from within opa_predict() (with @cookie supplied to + * the latter), on the event(s) that op-ahead predicts will happen. + */ + int (*act) (opa_event_t ev, int weight, void *cookie); + + /* + * Hash function supplied by the client. op-ahead uses this to keep an + * index of all events. + */ + __u32 (*hash) (opa_event_t ev); + /* + * Called by opa_happens() to find a guess corresponding to new + * event. This should return true if @ev is the same event as + * @pattern. + */ + int (*matches)(opa_event_t pattern, opa_event_t ev); + /* + * Dumps human-readable representation of event into @s, preceeded by + * @prefix and followed by @postfix. Used for /proc/opahead. + */ + void (*print) (struct seq_file *s, + const char *prefix, const char *postfix, + opa_event_t ev); + /* + * The depth of this domain. + */ + int depth; + /* + * GFP mask that op-ahead uses to allocate objects (paths, guesses, + * etc.) pertaining to this domain. + */ + int gfp; +}; + +/* + * Called to predict what events will happen after events in @context. If + * op-ahead comes up with plausible guess EVENT, it calls + * + * domain->act(EVENT, weight, cookie) + * + * method. If ->act() returns 0, prediction is continued: op-ahead assumes + * that EVENT actually happened and tries to make a next guess based on + * this. This continues until ->act() returns non-0, or op-ahead fails to make + * a reasonable guess. @context is not modified. + */ +void opa_predict(struct opa_domain *domain, opa_event_t *context, void *cookie); + +/* + * This is called to inform op-ahead that event @ev happened. The event is + * assumed to follow events in @context. opa_happens() updates @context and + * may acquire references on @ev. + */ +void opa_happens(struct opa_domain *domain, opa_event_t *context, + opa_event_t ev); +/* + * Called by the client to notify that @context will no longer be used. This + * releases references on all events in this path. + */ +void opa_context_put(struct opa_domain *domain, opa_event_t *context); + +#endif /* __KERNEL__ */ +#endif /* __OPAHEAD_H__ */ diff -puN /dev/null lib/opahead.c --- /dev/null 2004-04-06 17:27:52.000000000 +0400 +++ git-linux-nikita/lib/opahead.c 2005-11-20 15:48:39.000000000 +0300 @@ -0,0 +1,736 @@ +/* + * opahead.c - generic operation-ahead prediction engine + * + * Copyright (c) 2004 Nikita Danilov + * + * This file is released under the GPLv2. + * + */ + +/* + * Implementation of op-ahead. See for API and description. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define OPA_DEBUG (1) +#define OPA_PRINT (0) + +#if OPA_DEBUG + +#include +#include + +#define ASSERT(x) \ +({ \ + if (likely(x)) { \ + ; \ + } else { \ + printk(KERN_EMERG "%s(%i): %s:%i%s: assert(%s)\n", \ + current->comm, current->pid, \ + __FILE__, __LINE__, __FUNCTION__, #x); \ + panic("assertion failed\n"); \ + } \ + (void)0; \ +}) +#else +#define ASSERT(x) do {;} while(0) +#endif + +/* + * Guess about event that will follow some path. All guesses for a given path + * are hanging of list in the struct opa_path. + */ +struct opa_guess { + /* + * weight of this guess + */ + int weight; + /* + * event predicted by this guess + */ + opa_event_t ev; + /* + * linkage into path->guesses + */ + struct list_head linkage; +}; + +/* + * Sequence of domain->depth events and a list of guesses. + */ +struct opa_path { + int refcount; /* protected by opa_guard */ + struct opa_domain *domain; + /* + * hash table linkage. All paths are indexed in the global + * hash-table. A sum of domain-specific hashes for events is used as a + * hash for path. + */ + struct hlist_node linkage; + /* + * list of guesses, struct opa_guess'es are linked here. Ordered by + * guess weight. Protected by opa_guard. + */ + struct list_head guesses; + /* + * global LRU list of paths. Protected by opa_guard. + */ + struct list_head lru; + /* + * array of domain->depth events on the basis of which guess is made. + */ + opa_event_t head[0]; +}; + +/* + * Slab to allocate guesses from. + */ +static kmem_cache_t *opa_guess_slab; +/* + * Global spin-lock. Everything in op-ahead is protected by this. Bad. + */ +static spinlock_t opa_guard = SPIN_LOCK_UNLOCKED; +/* + * LRU of unused opahead paths (ones with zero refcount). Protected by + * opa_guard. On memory pressure this list is scanned by opa_path_shrinker() + * to keep LRU size under control. + */ +static LIST_HEAD(opa_path_lru); +/* + * Number of elements in the opa_path_lru. + */ +static unsigned int opa_path_cache = 0; + +/* + * Parameters of opa_path_table hash table. + */ +enum { + PATH_TABLE_SHIFT = 14, + PATH_TABLE_SIZE = (1 << PATH_TABLE_SHIFT), + PATH_TABLE_MASK = PATH_TABLE_SIZE - 1 +}; + +/* + * GLobal hash table where paths are indexed. Protected by opa_guard. + */ +struct hlist_head *opa_path_table; + +static int opa_path_shrinker(int nr_to_scan, unsigned int gfp_mask); + +/* + * Statistics collection stuff. + */ + +enum { + OPA_STATS_FORE = 6 +}; + +struct { + atomic_t paths_total; + atomic_t paths_allocated; + atomic_t guesses_allocated; + atomic_t guesses_total; + atomic_t predictions; + atomic_t matches; + atomic_t events; + atomic_t hits; + atomic_t misses; + struct { + atomic_t call; + atomic_t path; + } pred[OPA_STATS_FORE]; +} stats = { + .paths_total = ATOMIC_INIT(0), + .paths_allocated = ATOMIC_INIT(0), + .guesses_allocated = ATOMIC_INIT(0), + .guesses_total = ATOMIC_INIT(0), + .predictions = ATOMIC_INIT(0), + .matches = ATOMIC_INIT(0), + .events = ATOMIC_INIT(0), + .hits = ATOMIC_INIT(0), + .misses = ATOMIC_INIT(0), + .pred = { + { + .call = ATOMIC_INIT(0), + .path = ATOMIC_INIT(0), + }, + } +}; + +static int __init opa_init(void) +{ + int result; + + opa_guess_slab = kmem_cache_create("opa_guess", + sizeof(struct opa_guess), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (opa_guess_slab == NULL) + return -ENOMEM; + opa_path_table = kmalloc(PATH_TABLE_SIZE * sizeof(struct hlist_head), + GFP_KERNEL); + if (opa_path_table != NULL) { + int i; + struct shrinker *shrinker; + + for (i = 0; i < PATH_TABLE_SIZE; ++i) + INIT_HLIST_HEAD(&opa_path_table[i]); + shrinker = set_shrinker(DEFAULT_SEEKS, opa_path_shrinker); + if (shrinker != NULL) + result = 0; + else + result = -ENOMEM; + if (result != 0) { + kfree(opa_path_table); + opa_path_table = NULL; + } + } else + result = -ENOMEM; + if (result != 0) { + kmem_cache_destroy(opa_guess_slab); + opa_guess_slab = NULL; + } + return result; +} +__initcall(opa_init); + +static struct opa_guess *opa_guess_alloc(struct opa_domain *domain) +{ + struct opa_guess *guess; + + guess = kmem_cache_alloc(opa_guess_slab, domain->gfp); + if (guess != NULL) { + memset(guess, 0, sizeof *guess); + INIT_LIST_HEAD(&guess->linkage); + atomic_inc(&stats.guesses_allocated); + atomic_inc(&stats.guesses_total); + } + return guess; +} + +static void opa_guess_free(struct opa_domain *domain, struct opa_guess *guess) +{ + if (guess->ev != NULL) + domain->put(guess->ev); + list_del(&guess->linkage); + kmem_cache_free(opa_guess_slab, guess); + atomic_dec(&stats.guesses_allocated); +} + +static void opa_context_dup(struct opa_domain *domain, + opa_event_t *dst, opa_event_t *src) +{ + int i; + + for (i = 0; i < domain->depth; ++i) { + dst[i] = src[i]; + domain->get(dst[i]); + } +} + +void opa_context_put(struct opa_domain *domain, opa_event_t *ctx) +{ + int i; + + for (i = 0; i < domain->depth; ++i) { + if (ctx[i] != NULL) + domain->put(ctx[i]); + } +} + +static void opa_context_push(struct opa_domain *domain, + opa_event_t *ctx, opa_event_t ev) +{ + domain->put(ctx[0]); + memmove(ctx, ctx + 1, sizeof ctx[0] * (domain->depth - 1)); + ctx[domain->depth - 1] = ev; +} + +static struct opa_path *opa_path_alloc(struct opa_domain *domain) +{ + struct opa_path *path; + size_t size; + + size = sizeof *path + domain->depth * sizeof path->head[0]; + path = kmalloc(size, domain->gfp); + if (path != NULL) { + INIT_LIST_HEAD(&path->guesses); + INIT_HLIST_NODE(&path->linkage); + spin_lock(&opa_guard); + list_add_tail(&path->lru, &opa_path_lru); + spin_unlock(&opa_guard); + path->refcount = 1; + path->domain = domain; + atomic_inc(&stats.paths_allocated); + atomic_inc(&stats.paths_total); + } + return path; +} + +static struct opa_guess *opa_path_best(struct opa_path *path) +{ + if (!list_empty(&path->guesses)) + return container_of(path->guesses.next, + struct opa_guess, linkage); + else + return NULL; +} + +/* + * Free opahead path. Called under opa_guard. + */ +static void opa_path_free(struct opa_path *path) +{ + struct opa_domain *domain; + + domain = path->domain; + list_del(&path->lru); + + if (!hlist_unhashed(&path->linkage)) + hlist_del(&path->linkage); + + while (!list_empty(&path->guesses)) + opa_guess_free(domain, opa_path_best(path)); + + opa_context_put(domain, path->head); + + kfree(path); + atomic_dec(&stats.paths_allocated); +} + +static __u32 opa_hash(struct opa_domain *domain, opa_event_t *context) +{ + int i; + __u32 result; + + for (i = 0, result = 0; i < domain->depth; ++i) + result += domain->hash(context[i]); + result &= PATH_TABLE_MASK; + return result; +} + +/* + * Called under opa_guard. + */ +static struct opa_path *opa_path_look_at(struct opa_domain *domain, + opa_event_t *ctx, + struct hlist_head *bucket) +{ + struct opa_path *path; + struct hlist_node *scan; + + ASSERT(ctx != NULL); + ASSERT(bucket != NULL); + ASSERT(opa_path_table <= bucket && + bucket < opa_path_table + PATH_TABLE_SIZE); + + hlist_for_each_entry(path, scan, bucket, linkage) { + if (path->domain != domain) + continue; + if (!memcmp(ctx, path->head, sizeof ctx[0] * domain->depth)) { + if (path->refcount == 0) + -- opa_path_cache; + ++ path->refcount; + list_move_tail(&path->lru, &opa_path_lru); + return path; + } + } + return NULL; +} + +/* + * Called under opa_guard. + */ +static struct opa_path *opa_path_look(struct opa_domain *domain, + opa_event_t *context) +{ + struct opa_path *path; + struct hlist_head *bucket; + + bucket = &opa_path_table[opa_hash(domain, context)]; + + path = opa_path_look_at(domain, context, bucket); + return path; +} + +static struct opa_path *opa_path_get(struct opa_domain *domain, + opa_event_t *context) +{ + struct opa_path *path; + struct opa_path *shadow; + struct hlist_head *bucket; + + bucket = &opa_path_table[opa_hash(domain, context)]; + + spin_lock(&opa_guard); + path = opa_path_look_at(domain, context, bucket); + spin_unlock(&opa_guard); + + if (path != NULL) + return path; + + path = opa_path_alloc(domain); + if (path == NULL) + return NULL; + + opa_context_dup(domain, path->head, context); + + spin_lock(&opa_guard); + shadow = opa_path_look_at(domain, context, bucket); + if (shadow == NULL) + hlist_add_head(&path->linkage, bucket); + else { + opa_path_free(path); + path = shadow; + } + spin_unlock(&opa_guard); + return path; +} + +static void opa_path_put(struct opa_path *path) +{ + ASSERT(path->refcount > 0); + + spin_lock(&opa_guard); + -- path->refcount; + if (path->refcount == 0) + ++ opa_path_cache; + spin_unlock(&opa_guard); +} + +static int opa_path_shrinker(int nr_to_scan, unsigned int gfp_mask) +{ + struct opa_path *path; + struct opa_path *temp; + + spin_lock(&opa_guard); + list_for_each_entry_safe(path, temp, &opa_path_lru, lru) { + if (nr_to_scan-- == 0) + break; + if (path->refcount > 0) + continue; + opa_path_free(path); + ASSERT(opa_path_cache > 0); + -- opa_path_cache; + } + spin_unlock(&opa_guard); + return opa_path_cache; +} + +static struct opa_guess *opa_guess_look(struct opa_path *path, opa_event_t ev) +{ + struct opa_guess *guess; + + list_for_each_entry(guess, &path->guesses, linkage) { + if (path->domain->matches(guess->ev, ev)) { + atomic_inc(&stats.matches); + return guess; + } + } + return NULL; +} + +static struct opa_guess *opa_guess_get(struct opa_path *path, opa_event_t ev, + struct opa_guess **best) +{ + struct opa_guess *guess; + struct opa_guess *shadow; + struct opa_domain *domain; + + domain = path->domain; + spin_lock(&opa_guard); + *best = opa_path_best(path); + guess = opa_guess_look(path, ev); + spin_unlock(&opa_guard); + + if (guess != NULL) + return guess; + + guess = opa_guess_alloc(domain); + if (guess == NULL) + return NULL; + + spin_lock(&opa_guard); + shadow = opa_guess_look(path, ev); + if (shadow == NULL) { + list_add_tail(&guess->linkage, &path->guesses); + guess->ev = ev; + domain->get(ev); + } else { + kmem_cache_free(opa_guess_slab, guess); + guess = shadow; + } + spin_unlock(&opa_guard); + return guess; +} + +static void opa_guess_sort(struct opa_path *path, struct opa_guess *guess) +{ + struct opa_guess *scan; + + list_for_each_entry(scan, &path->guesses, linkage) { + if (scan == guess) + break; + if (scan->weight <= guess->weight) { + list_move_tail(&guess->linkage, &scan->linkage); + break; + } + } +} + +static void opa_path_hit(struct opa_path *path, opa_event_t ev) +{ + struct opa_guess *guess; + struct opa_guess *best; + + guess = opa_guess_get(path, ev, &best); + if (guess != NULL) { + spin_lock(&opa_guard); + ++ guess->weight; + if (guess != best) { + atomic_inc(&stats.misses); + /* -- best->weight; */ + } else + atomic_inc(&stats.hits); + opa_guess_sort(path, guess); + spin_unlock(&opa_guard); + } +} + +static void opa_guess_print(struct seq_file *s, + const char *prefix, const char *postfix, + struct opa_domain *domain, struct opa_guess *guess) +{ + seq_printf(s, "%s", prefix); + if (guess == NULL) + seq_printf(s, "nil"); + else { + seq_printf(s, "(%i", guess->weight); + domain->print(s, " ", ")", guess->ev); + } + seq_printf(s, "%s", postfix); +} + +static void opa_path_print(struct seq_file *s, + const char *prefix, const char *postfix, + struct opa_path *path) +{ + int i; + struct opa_guess *guess; + + seq_printf(s, "%s", prefix); + if (path == NULL) + seq_printf(s, "nil"); + else { + seq_printf(s, "(["); + for (i = 0; i < path->domain->depth; ++ i) + path->domain->print(s, i > 0 ? " " : "", "", + path->head[i]); + seq_printf(s, "] ["); + list_for_each_entry(guess, &path->guesses, linkage) + opa_guess_print(s, "", " ", path->domain, guess); + seq_printf(s, "])"); + } + seq_printf(s, "%s", postfix); +} + +#if OPA_PRINT +void opa_context_print(const char *prefix, const char *postfix, + struct opa_domain *domain, opa_event_t *context) +{ + int i; + + printk("%s", prefix); + if (context == NULL) + printk("nil"); + else { + printk("["); + for (i = 0; i < domain->depth; ++ i) + domain->print(i > 0 ? " " : "", "", context[i]); + printk("]"); + } + printk("%s", postfix); +} +/* OPA_PRINT */ +#else +#define opa_context_print(pre, post, d, c) +/* OPA_PRINT */ +#endif + +void opa_predict(struct opa_domain *domain, opa_event_t *context, void *cookie) +{ + struct opa_path *match; + int i; + /* + * XXX nikita: yes, I know this is bad. + */ + opa_event_t dup[domain->depth]; + + opa_context_print("opa_predict: ", "\n", domain, context); + + if (context[domain->depth - 1] == NULL) + /* + * This context hasn't yet enough elements to make a + * prediction. + */ + return; + + atomic_inc(&stats.predictions); + + opa_context_dup(domain, dup, context); + for (i = 0; ; ++ i) { + int result; + int idx; + + idx = min(i, OPA_STATS_FORE - 1); + atomic_inc(&stats.pred[idx].call); + + match = opa_path_look(domain, dup); + if (match != NULL) { + struct opa_guess *best; + + atomic_inc(&stats.pred[idx].path); + + spin_lock(&opa_guard); + best = opa_path_best(match); + spin_unlock(&opa_guard); + + if (best != NULL) { + domain->get(best->ev); + result = domain->act(best->ev, + best->weight, cookie); + opa_context_push(domain, dup, best->ev); + } else + result = 1; + opa_path_put(match); + if (result != 0) + break; + } else + break; + } + opa_context_put(domain, dup); +} + +void opa_happens(struct opa_domain *domain, opa_event_t *context, + opa_event_t ev) +{ + int pos; + + opa_context_print("opa_happens: ", "\n", domain, context); + + if (context[domain->depth - 1] != NULL) { + struct opa_path *match; + + atomic_inc(&stats.events); + match = opa_path_get(domain, context); + if (match != NULL) { + opa_path_hit(match, ev); + opa_path_put(match); + } + opa_context_push(domain, context, ev); + } else { + for (pos = 0; pos < domain->depth; ++ pos) { + if (context[pos] == NULL) + break; + } + BUG_ON(pos == domain->depth); + context[pos] = ev; + } +} + + +static int opa_show_stats(struct seq_file *p) +{ + int i; + + seq_printf(p, + "paths: %u/%u\n" + "guesses: %u/%u\n" + "predictions: %u\n" + "events: %u\n" + "matches: %u\n" + "hits: %u\n" + "misses: %u\n" + "cached: %u\n", + atomic_read(&stats.paths_allocated), + atomic_read(&stats.paths_total), + atomic_read(&stats.guesses_allocated), + atomic_read(&stats.guesses_total), + atomic_read(&stats.predictions), + atomic_read(&stats.events), + atomic_read(&stats.matches), + atomic_read(&stats.hits), + atomic_read(&stats.misses), + opa_path_cache); + for (i = 0; i < OPA_STATS_FORE; ++ i) { + seq_printf(p, "%i: call: %u path: %u\n", i, + atomic_read(&stats.pred[i].call), + atomic_read(&stats.pred[i].path)); + } + return 0; +} + +static void *path_start(struct seq_file *m, loff_t *pos) +{ + loff_t n; + struct opa_path *path; + + spin_lock(&opa_guard); + n = *pos; + + if (n-- == 0) + return (void *)1; + + list_for_each_entry(path, &opa_path_lru, lru) { + if (n-- == 0) + return path; + } + return NULL; +} + +static void *path_next(struct seq_file *m, void *p, loff_t *pos) +{ + struct opa_path *path; + struct list_head *punct; + + if (p == (void *)1) + punct = &opa_path_lru; + else + punct = &((struct opa_path *)p)->lru; + punct = punct->next; + if (punct == &opa_path_lru) + path = NULL; + else + path = container_of(punct, struct opa_path, lru); + ++ *pos; + return path; +} + +static void path_stop(struct seq_file *m, void *p) +{ + spin_unlock(&opa_guard); +} + +static int path_show(struct seq_file *m, void *p) +{ + if (p == (void *)1) + opa_show_stats(m); + else + opa_path_print(m, "", "\n", p); + return 0; +} + +struct seq_operations opa_seq_op = { + .start = path_start, + .next = path_next, + .stop = path_stop, + .show = path_show, +}; diff -puN lib/Makefile~opahead lib/Makefile --- git-linux/lib/Makefile~opahead 2005-11-19 20:12:39.000000000 +0300 +++ git-linux-nikita/lib/Makefile 2005-11-19 20:13:10.000000000 +0300 @@ -5,7 +5,7 @@ lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \ bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \ idr.o div64.o int_sqrt.o bitmap.o extable.o prio_tree.o \ - sha1.o do_with_stack.o + sha1.o do_with_stack.o opahead.o lib-y += kobject.o kref.o kobject_uevent.o klist.o diff -puN fs/proc/proc_misc.c~opahead fs/proc/proc_misc.c --- git-linux/fs/proc/proc_misc.c~opahead 2005-11-19 20:12:39.000000000 +0300 +++ git-linux-nikita/fs/proc/proc_misc.c 2005-11-19 20:12:39.000000000 +0300 @@ -233,6 +233,21 @@ static struct file_operations proc_zonei .release = seq_release, }; +extern struct seq_operations opa_seq_op; + +static int opahead_open(struct inode *inode, struct file *file) +{ + (void)inode; + return seq_open(file, &opa_seq_op); +} + +static struct file_operations proc_opahead_file_operations = { + .open = opahead_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + static int version_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -608,6 +623,7 @@ void __init proc_misc_init(void) create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations); create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations); create_seq_entry("zoneinfo",S_IRUGO, &proc_zoneinfo_file_operations); + create_seq_entry("opahead",S_IRUGO, &proc_opahead_file_operations); create_seq_entry("diskstats", 0, &proc_diskstats_operations); #ifdef CONFIG_MODULES create_seq_entry("modules", 0, &proc_modules_operations); _