diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/call.c linux-2.6.22.1/drivers/char/fusion/call.c --- linux-2.6.22.1-0rig/drivers/char/fusion/call.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/call.c 2007-01-20 05:03:01.000000000 +0100 @@ -0,0 +1,478 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifdef HAVE_LINUX_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include + +#include + +#include "fusiondev.h" +#include "fusionee.h" +#include "list.h" +#include "call.h" + +typedef struct { + FusionLink link; + + Fusionee *caller; + + int ret_val; + + bool executed; + + wait_queue_head_t wait; +} FusionCallExecution; + +typedef struct { + FusionLink link; + + struct semaphore lock; + + int id; /* call id */ + + int pid; /* owner pid */ + int fusion_id; /* owner fusion id */ + + void *handler; + void *ctx; + + FusionLink *executions; /* prepending! */ + FusionLink *last; /* points to the last item of executions */ + + int count; /* number of calls ever made */ +} FusionCall; + +/******************************************************************************/ + +static int lookup_call (FusionDev *dev, int id, FusionCall **ret_call); +static int lock_call (FusionDev *dev, int id, FusionCall **ret_call); +static void unlock_call (FusionCall *call); + +static FusionCallExecution *add_execution (FusionCall *call, + Fusionee *caller, + FusionCallExecute *execute); +static void remove_execution (FusionCall *call, + FusionCallExecution *execution); +static void free_all_executions (FusionCall *call); + +/******************************************************************************/ + +static int +fusion_call_read_proc (char *buf, char **start, off_t offset, + int len, int *eof, void *private) +{ + FusionLink *l, *e; + FusionDev *dev = private; + int written = 0; + + if (down_interruptible (&dev->call.lock)) + return -EINTR; + + fusion_list_foreach (l, dev->call.list) { + bool idle = true; + FusionCall *call = (FusionCall*) l; + + if (call->executions) + idle = ((FusionCallExecution*) call->executions)->executed; + + written += sprintf(buf+written, + "(%5d) 0x%08x (%d calls) %s", + call->pid, call->id, call->count, + idle ? "idle" : "executing"); + + fusion_list_foreach (e, call->executions) { + FusionCallExecution *exec = (FusionCallExecution *) e; + + written += sprintf(buf+written, " [0x%08lx]", exec->caller ? fusionee_id( exec->caller ) : 0); + } + + written += sprintf(buf+written, "\n"); + + if (written < offset) { + offset -= written; + written = 0; + } + + if (written >= len) + break; + } + + up (&dev->call.lock); + + *start = buf + offset; + written -= offset; + if (written > len) { + *eof = 0; + return len; + } + + *eof = 1; + return(written<0) ? 0 : written; +} + +int +fusion_call_init (FusionDev *dev) +{ + create_proc_read_entry("calls", 0, dev->proc_dir, + fusion_call_read_proc, dev); + + init_MUTEX(&dev->call.lock); + + return 0; +} + +void +fusion_call_deinit (FusionDev *dev) +{ + FusionLink *l; + + down (&dev->call.lock); + + remove_proc_entry ("calls", dev->proc_dir); + + l = dev->call.list; + while (l) { + FusionLink *next = l->next; + FusionCall *call = (FusionCall *) l; + + free_all_executions (call); + + kfree (call); + + l = next; + } + + up (&dev->call.lock); +} + +/******************************************************************************/ + +int +fusion_call_new (FusionDev *dev, int fusion_id, FusionCallNew *call_new) +{ + FusionCall *call; + + call = kmalloc (sizeof(FusionCall), GFP_KERNEL); + if (!call) + return -ENOMEM; + + memset (call, 0, sizeof(FusionCall)); + + if (down_interruptible (&dev->call.lock)) { + kfree (call); + return -EINTR; + } + + call->id = dev->call.ids++; + call->pid = current->pid; + call->fusion_id = fusion_id; + call->handler = call_new->handler; + call->ctx = call_new->ctx; + + init_MUTEX (&call->lock); + + fusion_list_prepend (&dev->call.list, &call->link); + + up (&dev->call.lock); + + call_new->call_id = call->id; + + return 0; +} + +int +fusion_call_execute (FusionDev *dev, Fusionee *fusionee, FusionCallExecute *execute) +{ + int ret; + FusionCall *call; + FusionCallExecution *execution; + FusionCallMessage message; + + ret = lock_call (dev, execute->call_id, &call); + if (ret) + return ret; + + execution = add_execution (call, fusionee, execute); + if (!execution) { + unlock_call (call); + return -ENOMEM; + } + + /* Send call message. */ + message.handler = call->handler; + message.ctx = call->ctx; + + message.caller = fusionee ? fusionee_id( fusionee ) : 0; + + message.call_arg = execute->call_arg; + message.call_ptr = execute->call_ptr; + + ret = fusionee_send_message (dev, fusionee, call->fusion_id, FMT_CALL, + call->id, sizeof(message), &message); + if (ret) { + remove_execution (call, execution); + kfree (execution); + unlock_call (call); + return ret; + } + + call->count++; + + if (fusionee && !(execute->flags & FCEF_ONEWAY)) { + /* TODO: implement timeout */ + fusion_sleep_on (&execution->wait, &call->lock, 0); + + ret = lock_call (dev, execute->call_id, &call); + if (ret) + return ret == -EINVAL ? -EIDRM : ret; + + if (signal_pending(current)) { + execution->caller = 0; + unlock_call (call); + return -EINTR; + } + + execute->ret_val = execution->ret_val; + + remove_execution (call, execution); + + kfree (execution); + } + + unlock_call (call); + + return 0; +} + +int +fusion_call_return (FusionDev *dev, int fusion_id, FusionCallReturn *call_ret) +{ + int ret; + FusionLink *l; + FusionCall *call; + + ret = lock_call (dev, call_ret->call_id, &call); + if (ret) + return ret; + + l = call->last; + while (l) { + FusionCallExecution *execution = (FusionCallExecution*) l; + + if (execution->executed) { + l = l->prev; + continue; + } + + if (execution->caller) { + execution->ret_val = call_ret->val; + execution->executed = true; + + wake_up_interruptible_all (&execution->wait); + } + else { + remove_execution (call, execution); + + kfree (execution); + } + + unlock_call (call); + + return 0; + } + + unlock_call (call); + + return -EIO; +} + +int +fusion_call_destroy (FusionDev *dev, int fusion_id, int call_id) +{ + int ret; + FusionCall *call; + + ret = lookup_call (dev, call_id, &call); + if (ret) + return ret; + + if (call->fusion_id != fusion_id) { + up (&dev->call.lock); + return -EIO; + } + + if (down_interruptible (&call->lock)) { + up (&dev->call.lock); + return -EINTR; + } + + fusion_list_remove (&dev->call.list, &call->link); + + free_all_executions (call); + + up (&dev->call.lock); + + up (&call->lock); + + kfree (call); + + return 0; +} + +void +fusion_call_destroy_all (FusionDev *dev, int fusion_id) +{ + FusionLink *l; + + down (&dev->call.lock); + + l = dev->call.list; + + while (l) { + FusionLink *next = l->next; + FusionCall *call = (FusionCall *) l; + + down (&call->lock); + + if (call->fusion_id == fusion_id) { + free_all_executions (call); + + fusion_list_remove (&dev->call.list, &call->link); + + up (&call->lock); + + kfree (call); + } + else + up (&call->lock); + + l = next; + } + + up (&dev->call.lock); +} + +/******************************************************************************/ + +static int +lookup_call (FusionDev *dev, int id, FusionCall **ret_call) +{ + FusionLink *l; + + if (down_interruptible (&dev->call.lock)) + return -EINTR; + + fusion_list_foreach (l, dev->call.list) { + FusionCall *call = (FusionCall *) l; + + if (call->id == id) { + *ret_call = call; + return 0; + } + } + + up (&dev->call.lock); + + return -EINVAL; +} + +static int +lock_call (FusionDev *dev, int id, FusionCall **ret_call) +{ + int ret; + FusionCall *call; + + ret = lookup_call (dev, id, &call); + if (ret) + return ret; + + if (call) { + fusion_list_move_to_front (&dev->call.list, &call->link); + + if (down_interruptible (&call->lock)) { + up (&dev->call.lock); + return -EINTR; + } + + up (&dev->call.lock); + } + + *ret_call = call; + + return 0; +} + +static void +unlock_call (FusionCall *call) +{ + up (&call->lock); +} + +static FusionCallExecution * +add_execution (FusionCall *call, + Fusionee *caller, + FusionCallExecute *execute) +{ + FusionCallExecution *execution; + + /* Allocate execution. */ + execution = kmalloc (sizeof(FusionCallExecution), GFP_KERNEL); + if (!execution) + return NULL; + + /* Initialize execution. */ + memset (execution, 0, sizeof(FusionCallExecution)); + + execution->caller = caller; + + init_waitqueue_head (&execution->wait); + + /* Add execution. */ + fusion_list_prepend (&call->executions, &execution->link); + + if (!call->last) + call->last = &execution->link; + + return execution; +} + +static void +remove_execution (FusionCall *call, + FusionCallExecution *execution) +{ + if (call->last == &execution->link) + call->last = execution->link.prev; + + fusion_list_remove (&call->executions, &execution->link); +} + +static void +free_all_executions (FusionCall *call) +{ + while (call->last) { + FusionCallExecution *execution = (FusionCallExecution *) call->last; + + remove_execution (call, execution); + + wake_up_interruptible_all (&execution->wait); + + kfree (execution); + } +} diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/call.h linux-2.6.22.1/drivers/char/fusion/call.h --- linux-2.6.22.1-0rig/drivers/char/fusion/call.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/call.h 2007-01-20 05:03:01.000000000 +0100 @@ -0,0 +1,52 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSION__CALL_H__ +#define __FUSION__CALL_H__ + +#include + +#include "fusiondev.h" + +/* module init/cleanup */ + +int fusion_call_init (FusionDev *dev); +void fusion_call_deinit (FusionDev *dev); + + +/* public API */ + +int fusion_call_new (FusionDev *dev, + int fusion_id, + FusionCallNew *call); + +int fusion_call_execute (FusionDev *dev, + Fusionee *fusionee, /* NULL if call is from kernel */ + FusionCallExecute *execute); + +int fusion_call_return (FusionDev *dev, + int fusion_id, + FusionCallReturn *call_ret); + +int fusion_call_destroy (FusionDev *dev, + int fusion_id, + int call_id); + + +/* internal functions */ + +void fusion_call_destroy_all (FusionDev *dev, + int fusion_id); + +#endif diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/.cvsignore linux-2.6.22.1/drivers/char/fusion/.cvsignore --- linux-2.6.22.1-0rig/drivers/char/fusion/.cvsignore 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/.cvsignore 2005-10-29 07:11:10.000000000 +0200 @@ -0,0 +1,6 @@ +*.o.flags +*.cmd +Makefile +fusion.ko +fusion.mod.c +.tmp_versions diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/entries.c linux-2.6.22.1/drivers/char/fusion/entries.c --- linux-2.6.22.1-0rig/drivers/char/fusion/entries.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/entries.c 2007-03-08 14:02:04.000000000 +0100 @@ -0,0 +1,438 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifdef HAVE_LINUX_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fusiondev.h" +#include "entries.h" + + +void +fusion_entries_init( FusionEntries *entries, + FusionEntryClass *class, + void *ctx ) +{ + FUSION_ASSERT( entries != NULL ); + FUSION_ASSERT( class != NULL ); + FUSION_ASSERT( class->object_size >= sizeof(FusionEntry) ); + + memset( entries, 0, sizeof(FusionEntries) ); + + entries->class = class; + entries->ctx = ctx; + + init_MUTEX( &entries->lock ); +} + +void +fusion_entries_deinit( FusionEntries *entries ) +{ + FusionLink *tmp; + FusionEntry *entry; + FusionEntryClass *class; + + FUSION_ASSERT( entries != NULL ); + FUSION_ASSERT( entries->class != NULL ); + + class = entries->class; + + down( &entries->lock ); + + fusion_list_foreach_safe (entry, tmp, entries->list) { + if (class->Destroy) + class->Destroy( entry, entries->ctx ); + + kfree( entry ); + } + + up( &entries->lock ); +} + +int +fusion_entries_read_proc(char *buf, char **start, off_t offset, + int len, int *eof, void *private) +{ + FusionEntry *entry; + FusionEntryClass *class; + FusionEntries *entries = private; + int written = 0; + struct timeval now; + + FUSION_ASSERT( entries != NULL ); + FUSION_ASSERT( entries->class != NULL ); + + class = entries->class; + + if (!class->Print) + return -ENOTSUPP; + + if (down_interruptible (&entries->lock)) + return -EINTR; + + do_gettimeofday( &now ); + + fusion_list_foreach (entry, entries->list) { + if (entry->last_lock.tv_sec) { + int diff = ((now.tv_sec - entry->last_lock.tv_sec) * 1000 + + (now.tv_usec - entry->last_lock.tv_usec) / 1000); + + if (diff < 1000) { + written += sprintf( buf + written, "%3d ms ", diff ); + } + else if (diff < 1000000) { + written += sprintf( buf + written, "%3d.%d s ", + diff / 1000, (diff % 1000) / 100 ); + } + else { + diff = ( now.tv_sec - entry->last_lock.tv_sec + + (now.tv_usec - entry->last_lock.tv_usec) / 1000000); + + written += sprintf( buf + written, "%3d.%d h ", + diff / 3600, (diff % 3600) / 360 ); + } + } + else + written += sprintf( buf + written, " -.- " ); + + + written += sprintf( buf + written, "(%5d) 0x%08x ", entry->pid, entry->id ); + + written += sprintf( buf + written, "%-24s ", entry->name[0] ? entry->name : "" ); + + written += class->Print( entry, entries->ctx, buf + written ); + + if (written < offset) { + offset -= written; + written = 0; + } + + if (written >= len) + break; + } + + up (&entries->lock); + + *start = buf + offset; + written -= offset; + if (written > len) { + *eof = 0; + return len; + } + + *eof = 1; + + return (written<0) ? 0 : written; +} + +int +fusion_entry_create( FusionEntries *entries, + int *ret_id, + void *create_ctx ) +{ + int ret; + FusionEntry *entry; + FusionEntryClass *class; + + FUSION_ASSERT( entries != NULL ); + FUSION_ASSERT( entries->class != NULL ); + FUSION_ASSERT( ret_id != NULL ); + + class = entries->class; + + entry = kmalloc( class->object_size, GFP_KERNEL ); + if (!entry) + return -ENOMEM; + + memset( entry, 0, class->object_size ); + + if (down_interruptible( &entries->lock )) { + kfree( entry ); + return -EINTR; + } + + entry->entries = entries; + entry->id = entries->ids++; + entry->pid = current->pid; + + init_MUTEX( &entry->lock ); + + init_waitqueue_head( &entry->wait ); + + if (class->Init) { + ret = class->Init( entry, entries->ctx, create_ctx ); + if (ret) { + up( &entries->lock ); + kfree( entry ); + return ret; + } + } + + fusion_list_prepend( &entries->list, &entry->link ); + + up( &entries->lock ); + + *ret_id = entry->id; + + return 0; +} + +int +fusion_entry_destroy( FusionEntries *entries, + int id ) +{ + FusionEntry *entry; + FusionEntryClass *class; + + FUSION_ASSERT( entries != NULL ); + FUSION_ASSERT( entries->class != NULL ); + + class = entries->class; + + /* Lock entries. */ + if (down_interruptible( &entries->lock )) + return -EINTR; + + /* Lookup the entry. */ + fusion_list_foreach (entry, entries->list) { + if (entry->id == id) + break; + } + + /* Check if no entry was found. */ + if (!entry) { + up( &entries->lock ); + return -EINVAL; + } + + /* Lock the entry. */ + if (down_interruptible( &entry->lock )) { + up( &entries->lock ); + return -EINTR; + } + + /* Destroy it now. */ + fusion_entry_destroy_locked( entries, entry ); + + /* Unlock entries. */ + up( &entries->lock ); + + return 0; +} + +void +fusion_entry_destroy_locked( FusionEntries *entries, + FusionEntry *entry ) +{ + FusionEntryClass *class; + + FUSION_ASSERT( entries != NULL ); + FUSION_ASSERT( entries->class != NULL ); + + class = entries->class; + + /* Remove the entry from the list. */ + fusion_list_remove( &entries->list, &entry->link ); + + /* Wake up any waiting process. */ + wake_up_interruptible_all( &entry->wait ); + + /* Call the destroy function. */ + if (class->Destroy) + class->Destroy( entry, entries->ctx ); + + /* Unlock the entry. */ + up( &entry->lock ); + + /* Deallocate the entry. */ + kfree( entry ); +} + +int +fusion_entry_set_info( FusionEntries *entries, + const FusionEntryInfo *info ) +{ + int ret; + FusionEntry *entry; + + FUSION_ASSERT( entries != NULL ); + FUSION_ASSERT( info != NULL ); + + ret = fusion_entry_lock( entries, info->id, false, &entry ); + if (ret) + return ret; + + snprintf( entry->name, FUSION_ENTRY_INFO_NAME_LENGTH, info->name ); + + fusion_entry_unlock( entry ); + + return 0; +} + +int +fusion_entry_get_info( FusionEntries *entries, + FusionEntryInfo *info ) +{ + int ret; + FusionEntry *entry; + + FUSION_ASSERT( entries != NULL ); + FUSION_ASSERT( info != NULL ); + + ret = fusion_entry_lock( entries, info->id, false, &entry ); + if (ret) + return ret; + + snprintf( info->name, FUSION_ENTRY_INFO_NAME_LENGTH, entry->name ); + + fusion_entry_unlock( entry ); + + return 0; +} + +int +fusion_entry_lock( FusionEntries *entries, + int id, + bool keep_entries_lock, + FusionEntry **ret_entry ) +{ + FusionEntry *entry; + + FUSION_ASSERT( entries != NULL ); + FUSION_ASSERT( ret_entry != NULL ); + + /* Lock entries. */ + if (down_interruptible( &entries->lock )) + return -EINTR; + + /* Lookup the entry. */ + fusion_list_foreach (entry, entries->list) { + if (entry->id == id) + break; + } + + /* Check if no entry was found. */ + if (!entry) { + up( &entries->lock ); + return -EINVAL; + } + + FUSION_ASSUME( entry->lock_pid != current->pid ); + + /* Move the entry to the front of all entries. */ + fusion_list_move_to_front( &entries->list, &entry->link ); + + /* Lock the entry. */ + if (down_interruptible( &entry->lock )) { + up( &entries->lock ); + return -EINTR; + } + + /* Mark as locked. */ + entry->lock_pid = current->pid; + + /* Keep timestamp, but use the slightly + inexact version to avoid performance impacts. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && defined _STRUCT_TIMESPEC + entry->last_lock.tv_sec = xtime.tv_sec; + entry->last_lock.tv_usec = xtime.tv_nsec / 1000; +#else + entry->last_lock = xtime; +#endif + + /* Unlock entries. */ + if (!keep_entries_lock) + up( &entries->lock ); + + /* Return the locked entry. */ + *ret_entry = entry; + + return 0; +} + +void +fusion_entry_unlock( FusionEntry *entry ) +{ + FUSION_ASSERT( entry != NULL ); + FUSION_ASSUME( entry->lock_pid == current->pid ); + + entry->lock_pid = 0; + + /* Unlock the entry. */ + up( &entry->lock ); +} + +int +fusion_entry_wait( FusionEntry *entry, long *timeout ) +{ + int ret; + int id; + FusionEntries *entries; + FusionEntry *entry2; + + FUSION_ASSERT( entry != NULL ); + FUSION_ASSERT( entry->entries != NULL ); + FUSION_ASSUME( entry->lock_pid == current->pid ); + + id = entry->id; + entries = entry->entries; + + entry->waiters++; + + entry->lock_pid = 0; + fusion_sleep_on( &entry->wait, &entry->lock, timeout ); + + entry->waiters--; + + if (signal_pending(current)) + return -EINTR; + + if (timeout && !*timeout) + return -ETIMEDOUT; + + ret = fusion_entry_lock( entries, id, false, &entry2 ); + switch (ret) { + case -EINVAL: + return -EIDRM; + + case 0: + if (entry != entry2) + BUG(); + } + + return ret; +} + +void +fusion_entry_notify( FusionEntry *entry, bool all ) +{ + FUSION_ASSERT( entry != NULL ); + FUSION_ASSUME( entry->lock_pid == current->pid ); + + if (all) + wake_up_interruptible_all( &entry->wait ); + else + wake_up_interruptible( &entry->wait ); +} + diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/entries.h linux-2.6.22.1/drivers/char/fusion/entries.h --- linux-2.6.22.1-0rig/drivers/char/fusion/entries.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/entries.h 2006-09-03 13:50:55.000000000 +0200 @@ -0,0 +1,179 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSION__ENTRIES_H__ +#define __FUSION__ENTRIES_H__ + +#include "types.h" +#include "list.h" + + +typedef struct __FD_FusionEntry FusionEntry; + + +typedef const struct { + int object_size; + + int (*Init) ( FusionEntry *entry, void *ctx, void *create_ctx ); + void (*Destroy)( FusionEntry *entry, void *ctx ); + int (*Print) ( FusionEntry *entry, void *ctx, char *buf ); +} FusionEntryClass; + + +typedef struct { + FusionEntryClass *class; + void *ctx; + + FusionLink *list; + int ids; + struct semaphore lock; +} FusionEntries; + + +struct __FD_FusionEntry { + FusionLink link; + + FusionEntries *entries; + + int id; + pid_t pid; + + pid_t lock_pid; + + struct semaphore lock; + wait_queue_head_t wait; + int waiters; + + struct timeval last_lock; + + char name[FUSION_ENTRY_INFO_NAME_LENGTH]; +}; + + +/* Entries Init & DeInit */ + +void fusion_entries_init ( FusionEntries *entries, + FusionEntryClass *class, + void *ctx ); + +void fusion_entries_deinit( FusionEntries *entries ); + + +/* '/proc' support */ + +int fusion_entries_read_proc( char *buf, char **start, off_t offset, + int len, int *eof, void *private ); + + +/* Create & Destroy */ + +int fusion_entry_create ( FusionEntries *entries, + int *ret_id, + void *create_ctx ); + +int fusion_entry_destroy ( FusionEntries *entries, + int id ); + +void fusion_entry_destroy_locked( FusionEntries *entries, + FusionEntry *entry ); + +/* Information */ + +int fusion_entry_set_info( FusionEntries *entries, + const FusionEntryInfo *info ); + +int fusion_entry_get_info( FusionEntries *entries, + FusionEntryInfo *info ); + + +/* Lock & Unlock */ + +int fusion_entry_lock ( FusionEntries *entries, + int id, + bool keep_entries_lock, + FusionEntry **ret_entry ); + +void fusion_entry_unlock ( FusionEntry *entry ); + + +/** Wait & Notify **/ + +/* + * Wait for the entry to be notified with an optional timeout. + * + * The entry + * (1) has to be locked prior to calling this function. + * (2) is temporarily unlocked while being waited for. + * + * If this function returns an error, the entry is not locked again! + * + * Possible errors are: + * -EIDRM Entry has been removed while being waited for. + * -ETIMEDOUT Timeout occured. + * -EINTR A signal has been received. + */ +int fusion_entry_wait ( FusionEntry *entry, + long *timeout ); + +/* + * Wake up one or all processes waiting for the entry to be notified. + * + * The entry has to be locked prior to calling this function. + */ +void fusion_entry_notify ( FusionEntry *entry, + bool all ); + + +#define FUSION_ENTRY_CLASS( Type, name, init_func, destroy_func, print_func ) \ + \ + static FusionEntryClass name##_class = { \ + .object_size = sizeof(Type), \ + .Init = init_func, \ + .Destroy = destroy_func, \ + .Print = print_func \ + }; \ + \ + static inline int fusion_##name##_lock( FusionEntries *entries, \ + int id, \ + bool keep, \ + Type **ret_##name ) \ + { \ + int ret; \ + FusionEntry *entry; \ + \ + ret = fusion_entry_lock( entries, id, keep, &entry ); \ + \ + if (!ret) \ + *ret_##name = (Type *) entry; \ + \ + return ret; \ + } \ + \ + static inline void fusion_##name##_unlock( Type *name ) \ + { \ + fusion_entry_unlock( (FusionEntry*) name ); \ + } \ + \ + static inline int fusion_##name##_wait( Type *name, long *timeout ) \ + { \ + return fusion_entry_wait( (FusionEntry*) name, timeout ); \ + } \ + \ + static inline void fusion_##name##_notify( Type *name, bool all ) \ + { \ + fusion_entry_notify( (FusionEntry*) name, all ); \ + } + + +#endif diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/fifo.c linux-2.6.22.1/drivers/char/fusion/fifo.c --- linux-2.6.22.1-0rig/drivers/char/fusion/fifo.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/fifo.c 2003-06-16 19:47:03.000000000 +0200 @@ -0,0 +1,53 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +#include "fifo.h" + +void +fusion_fifo_put (FusionFifo *fifo, FusionLink *link) +{ + link->prev = fifo->last; + link->next = NULL; + + if (fifo->last) + fifo->last->next = link; + else + fifo->first = link; + + fifo->last = link; + + fifo->count++; +} + +FusionLink * +fusion_fifo_get (FusionFifo *fifo) +{ + FusionLink *first = fifo->first; + + if (!first) + return NULL; + + fifo->first = first->next; + + if (fifo->last == first) + fifo->last = NULL; + else + fifo->first->prev = NULL; + + fifo->count--; + + return first; +} diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/fifo.h linux-2.6.22.1/drivers/char/fusion/fifo.h --- linux-2.6.22.1-0rig/drivers/char/fusion/fifo.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/fifo.h 2003-06-16 19:47:03.000000000 +0200 @@ -0,0 +1,36 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSION__FIFO_H__ +#define __FUSION__FIFO_H__ + +#include "types.h" +#include "list.h" + +typedef struct { + FusionLink *first; + FusionLink *last; + + int count; +} FusionFifo; + +void fusion_fifo_put (FusionFifo *fifo, + FusionLink *link); + +FusionLink *fusion_fifo_get (FusionFifo *fifo); + +int fusion_fifo_count (FusionFifo *fifo); + +#endif /* __FUSION__LIST_H__ */ + diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/fusiondev.c linux-2.6.22.1/drivers/char/fusion/fusiondev.c --- linux-2.6.22.1-0rig/drivers/char/fusion/fusiondev.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/fusiondev.c 2007-03-08 14:02:47.000000000 +0100 @@ -0,0 +1,1187 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#ifdef HAVE_LINUX_CONFIG_H +#include +#endif +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) +#include +#endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 19) +#include +#include +#endif + +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 2) +#include +#endif + +#include + +#include "call.h" +#include "fusiondev.h" +#include "fusionee.h" +#include "property.h" +#include "reactor.h" +#include "ref.h" +#include "skirmish.h" +#include "shmpool.h" + +#if 0 +#define DEBUG(x...) printk (KERN_DEBUG "Fusion: " x) +#else +#define DEBUG(x...) do {} while (0) +#endif + +#ifndef FUSION_MAJOR +#define FUSION_MAJOR 252 +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Denis Oliver Kropp "); + +struct proc_dir_entry *proc_fusion_dir; + +#define NUM_MINORS 8 + +static FusionDev *fusion_devs[NUM_MINORS] = { 0 }; +static DECLARE_MUTEX(devs_lock); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) +static devfs_handle_t devfs_handles[NUM_MINORS]; +static inline unsigned iminor(struct inode *inode) +{ + return MINOR(inode->i_rdev); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 2) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) +static struct class *fusion_class; +#else +static struct class_simple *fusion_class; +#endif +#endif + +/******************************************************************************/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +void +fusion_sleep_on(wait_queue_head_t *q, struct semaphore *lock, signed long *timeout) +{ + DEFINE_WAIT(wait); + + prepare_to_wait( q, &wait, TASK_INTERRUPTIBLE ); + + up( lock ); + + if (timeout) + *timeout = schedule_timeout(*timeout); + else + schedule(); + + finish_wait( q, &wait ); +} +#else +void +fusion_sleep_on(wait_queue_head_t *q, struct semaphore *lock, signed long *timeout) +{ + wait_queue_t wait; + + init_waitqueue_entry (&wait, current); + + current->state = TASK_INTERRUPTIBLE; + + write_lock (&q->lock); + __add_wait_queue (q, &wait); + write_unlock (&q->lock); + + up (lock); + + if (timeout) + *timeout = schedule_timeout(*timeout); + else + schedule(); + + write_lock (&q->lock); + __remove_wait_queue (q, &wait); + write_unlock (&q->lock); +} +#endif + +/******************************************************************************/ + +static int +fusiondev_stat_read_proc(char *buf, char **start, off_t offset, + int len, int *eof, void *private) +{ + FusionDev *dev = private; + int written = 0; + + written += snprintf( buf, len, + "lease/purchase cede attach detach " + "ref up ref down prevail/swoop dismiss\n" ); + if (written < offset) { + offset -= written; + written = 0; + } + + if (written < len) { + written += snprintf( buf+written, len - written, + "%10d %10d %10d %10d %10d %10d %10d %10d\n", + dev->stat.property_lease_purchase, + dev->stat.property_cede, + dev->stat.reactor_attach, + dev->stat.reactor_detach, + dev->stat.ref_up, + dev->stat.ref_down, + dev->stat.skirmish_prevail_swoop, + dev->stat.skirmish_dismiss ); + if (written < offset) { + offset -= written; + written = 0; + } + } + + *start = buf + offset; + written -= offset; + if (written > len) { + *eof = 0; + return len; + } + + *eof = 1; + return(written<0) ? 0 : written; +} + +/******************************************************************************/ + +static int +fusiondev_init (FusionDev *dev) +{ + int ret; + + init_MUTEX( &dev->enter_lock ); + init_waitqueue_head( &dev->enter_wait ); + + ret = fusionee_init (dev); + if (ret) + goto error_fusionee; + + ret = fusion_ref_init (dev); + if (ret) + goto error_ref; + + ret = fusion_skirmish_init (dev); + if (ret) + goto error_skirmish; + + ret = fusion_property_init (dev); + if (ret) + goto error_property; + + ret = fusion_reactor_init (dev); + if (ret) + goto error_reactor; + + ret = fusion_shmpool_init (dev); + if (ret) + goto error_shmpool; + + ret = fusion_call_init (dev); + if (ret) + goto error_call; + + create_proc_read_entry( "stat", 0, dev->proc_dir, + fusiondev_stat_read_proc, dev ); + + return 0; + + +error_call: + fusion_shmpool_deinit (dev); + +error_shmpool: + fusion_reactor_deinit (dev); + +error_reactor: + fusion_property_deinit (dev); + +error_property: + fusion_skirmish_deinit (dev); + +error_skirmish: + fusion_ref_deinit (dev); + +error_ref: + fusionee_deinit (dev); + +error_fusionee: + return ret; +} + +static void +fusiondev_deinit (FusionDev *dev) +{ + remove_proc_entry ("stat", dev->proc_dir); + + fusion_call_deinit (dev); + fusion_shmpool_deinit (dev); + fusion_reactor_deinit (dev); + fusion_property_deinit (dev); + fusion_skirmish_deinit (dev); + fusion_ref_deinit (dev); + fusionee_deinit (dev); + + if (dev->shared_area) { + ClearPageReserved( virt_to_page(dev->shared_area) ); + free_page( dev->shared_area ); + } +} + +/******************************************************************************/ + +static int +fusion_open (struct inode *inode, struct file *file) +{ + int ret; + Fusionee *fusionee; + int minor = iminor(inode); + + DEBUG( "fusion_open( %p, %d )\n", file, atomic_read(&file->f_count) ); + + if (down_interruptible (&devs_lock)) + return -EINTR; + + if (!fusion_devs[minor]) { + char buf[4]; + + fusion_devs[minor] = kmalloc (sizeof(FusionDev), GFP_KERNEL); + if (!fusion_devs[minor]) { + up (&devs_lock); + return -ENOMEM; + } + + memset (fusion_devs[minor], 0, sizeof(FusionDev)); + + snprintf (buf, 4, "%d", minor); + + fusion_devs[minor]->proc_dir = proc_mkdir (buf, proc_fusion_dir); + fusion_devs[minor]->index = minor; + + ret = fusiondev_init (fusion_devs[minor]); + if (ret) { + remove_proc_entry (buf, proc_fusion_dir); + + kfree (fusion_devs[minor]); + fusion_devs[minor] = NULL; + + up (&devs_lock); + + return ret; + } + } + else if (file->f_flags & O_EXCL) { + if (fusion_devs[minor]->fusionee.last_id) { + up (&devs_lock); + return -EBUSY; + } + } + + ret = fusionee_new (fusion_devs[minor], !!(file->f_flags & O_APPEND), &fusionee); + if (ret) { + if (!fusion_devs[minor]->refs) { + fusiondev_deinit (fusion_devs[minor]); + + remove_proc_entry (fusion_devs[minor]->proc_dir->name, + proc_fusion_dir); + + kfree (fusion_devs[minor]); + fusion_devs[minor] = NULL; + } + + up (&devs_lock); + + return ret; + } + + fusion_devs[minor]->refs++; + + up (&devs_lock); + + + file->private_data = fusionee; + + return 0; +} + +static int +fusion_release (struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + Fusionee *fusionee = file->private_data; + + DEBUG( "fusion_release( %p, %d )\n", file, atomic_read(&file->f_count) ); + + fusionee_destroy (fusion_devs[minor], fusionee); + + down (&devs_lock); + + if (! --fusion_devs[minor]->refs) { + fusiondev_deinit (fusion_devs[minor]); + + remove_proc_entry (fusion_devs[minor]->proc_dir->name, + proc_fusion_dir); + + kfree (fusion_devs[minor]); + fusion_devs[minor] = NULL; + } + + up (&devs_lock); + + return 0; +} + +static int +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) +fusion_flush (struct file *file, fl_owner_t id) +#else +fusion_flush (struct file *file) +#endif +{ + Fusionee *fusionee = file->private_data; + FusionDev *dev = fusion_devs[iminor(file->f_dentry->d_inode)]; + + (void) fusionee; + + DEBUG( "fusion_flush( %p, %d, 0x%08x %d )\n", file, atomic_read(&file->f_count), fusionee_id(fusionee), current->pid ); + + if (current->flags & PF_EXITING) + fusion_skirmish_dismiss_all_from_pid (dev, current->pid); + + return 0; +} + +static ssize_t +fusion_read (struct file *file, char *buf, size_t count, loff_t *ppos) +{ + Fusionee *fusionee = file->private_data; + FusionDev *dev = fusion_devs[iminor(file->f_dentry->d_inode)]; + + DEBUG( "fusion_read( %p, %d, %d )\n", file, atomic_read(&file->f_count), count ); + + return fusionee_get_messages (dev, fusionee, buf, count, + !(file->f_flags & O_NONBLOCK)); +} + +static unsigned int +fusion_poll (struct file *file, poll_table * wait) +{ + Fusionee *fusionee = file->private_data; + FusionDev *dev = fusion_devs[iminor(file->f_dentry->d_inode)]; + + DEBUG( "fusion_poll( %p, %d )\n", file, atomic_read(&file->f_count) ); + + return fusionee_poll (dev, fusionee, file, wait); +} + +static int +lounge_ioctl (struct file *file, FusionDev *dev, Fusionee *fusionee, + unsigned int cmd, unsigned long arg) +{ + int ret; + FusionEnter enter; + FusionKill kill; + FusionEntryInfo info; + FusionFork fork = {0}; + + switch (_IOC_NR(cmd)) { + case _IOC_NR(FUSION_ENTER): + if (copy_from_user (&enter, (FusionEnter*) arg, sizeof(enter))) + return -EFAULT; + + ret = fusionee_enter( dev, &enter, fusionee ); + if (ret) + return ret; + + if (copy_to_user ((FusionEnter*) arg, &enter, sizeof(enter))) + return -EFAULT; + + return 0; + + case _IOC_NR(FUSION_UNBLOCK): + if (fusionee_id( fusionee ) != FUSION_ID_MASTER) + return -EPERM; + + if (down_interruptible( &dev->enter_lock )) + return -EINTR; + + dev->enter_ok = 1; + + wake_up_interruptible_all( &dev->enter_wait ); + + up( &dev->enter_lock ); + + return 0; + + case _IOC_NR(FUSION_KILL): + if (copy_from_user (&kill, (FusionKill*) arg, sizeof(kill))) + return -EFAULT; + + return fusionee_kill (dev, fusionee, + kill.fusion_id, kill.signal, kill.timeout_ms); + + case _IOC_NR(FUSION_ENTRY_SET_INFO): + if (copy_from_user (&info, (FusionEntryInfo*) arg, sizeof(info))) + return -EFAULT; + + switch (info.type) { + case FT_SKIRMISH: + return fusion_entry_set_info (&dev->skirmish, &info); + + case FT_PROPERTY: + return fusion_entry_set_info (&dev->properties, &info); + + case FT_REACTOR: + return fusion_entry_set_info (&dev->reactor, &info); + + case FT_REF: + return fusion_entry_set_info (&dev->ref, &info); + + case FT_SHMPOOL: + return fusion_entry_set_info (&dev->shmpool, &info); + + default: + return -ENOSYS; + } + + case _IOC_NR(FUSION_ENTRY_GET_INFO): + if (copy_from_user (&info, (FusionEntryInfo*) arg, sizeof(info))) + return -EFAULT; + + switch (info.type) { + case FT_SKIRMISH: + ret = fusion_entry_get_info (&dev->skirmish, &info); + break; + + case FT_PROPERTY: + ret = fusion_entry_get_info (&dev->properties, &info); + break; + + case FT_REACTOR: + ret = fusion_entry_get_info (&dev->reactor, &info); + break; + + case FT_REF: + ret = fusion_entry_get_info (&dev->ref, &info); + break; + + case FT_SHMPOOL: + ret = fusion_entry_get_info (&dev->shmpool, &info); + break; + + default: + return -ENOSYS; + } + + if (ret) + return ret; + + if (copy_to_user ((FusionEntryInfo*) arg, &info, sizeof(info))) + return -EFAULT; + + return 0; + + case _IOC_NR(FUSION_FORK): + if (copy_from_user( &fork, (FusionFork*) arg, sizeof(fork) )) + return -EFAULT; + + ret = fusionee_fork( dev, &fork, fusionee ); + if (ret) + return ret; + + if (copy_to_user( (FusionFork*) arg, &fork, sizeof(fork) )) + return -EFAULT; + + return 0; + } + + return -ENOSYS; +} + +static int +messaging_ioctl (FusionDev *dev, Fusionee *fusionee, + unsigned int cmd, unsigned long arg) +{ + FusionSendMessage send; + + switch (_IOC_NR(cmd)) { + case _IOC_NR(FUSION_SEND_MESSAGE): + if (copy_from_user (&send, (FusionSendMessage*) arg, sizeof(send))) + return -EFAULT; + + if (send.msg_size <= 0) + return -EINVAL; + + /* message data > 64k should be stored in shared memory */ + if (send.msg_size > 0x10000) + return -EMSGSIZE; + + return fusionee_send_message (dev, fusionee, send.fusion_id, FMT_SEND, + send.msg_id, send.msg_size, send.msg_data); + } + + return -ENOSYS; +} + +static int +call_ioctl (FusionDev *dev, Fusionee *fusionee, + unsigned int cmd, unsigned long arg) +{ + int id; + int ret; + FusionCallNew call; + FusionCallExecute execute; + FusionCallReturn call_ret; + FusionID fusion_id = fusionee_id( fusionee ); + + switch (_IOC_NR(cmd)) { + case _IOC_NR(FUSION_CALL_NEW): + if (copy_from_user (&call, (FusionCallNew*) arg, sizeof(call))) + return -EFAULT; + + ret = fusion_call_new (dev, fusion_id, &call); + if (ret) + return ret; + + if (put_user (call.call_id, (int*) arg)) { + fusion_call_destroy (dev, fusion_id, call.call_id); + return -EFAULT; + } + return 0; + + case _IOC_NR(FUSION_CALL_EXECUTE): + if (copy_from_user (&execute, (FusionCallExecute*) arg, sizeof(execute))) + return -EFAULT; + + ret = fusion_call_execute (dev, fusionee, &execute); + if (ret) + return ret; + + if (put_user (execute.ret_val, (int*) arg)) + return -EFAULT; + return 0; + + case _IOC_NR(FUSION_CALL_RETURN): + if (copy_from_user (&call_ret, (FusionCallReturn*) arg, sizeof(call_ret))) + return -EFAULT; + + return fusion_call_return (dev, fusion_id, &call_ret); + + case _IOC_NR(FUSION_CALL_DESTROY): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_call_destroy (dev, fusion_id, id); + } + + return -ENOSYS; +} + +static int +ref_ioctl (FusionDev *dev, Fusionee *fusionee, + unsigned int cmd, unsigned long arg) +{ + int id; + int ret; + int refs; + FusionRefWatch watch; + FusionRefInherit inherit; + FusionID fusion_id = fusionee_id( fusionee ); + + switch (_IOC_NR(cmd)) { + case _IOC_NR(FUSION_REF_NEW): + ret = fusion_ref_new (dev, &id); + if (ret) + return ret; + + if (put_user (id, (int*) arg)) { + fusion_ref_destroy (dev, id); + return -EFAULT; + } + return 0; + + case _IOC_NR(FUSION_REF_UP): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_ref_up (dev, id, fusion_id); + + case _IOC_NR(FUSION_REF_UP_GLOBAL): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_ref_up (dev, id, 0); + + case _IOC_NR(FUSION_REF_DOWN): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_ref_down (dev, id, fusion_id); + + case _IOC_NR(FUSION_REF_DOWN_GLOBAL): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_ref_down (dev, id, 0); + + case _IOC_NR(FUSION_REF_ZERO_LOCK): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_ref_zero_lock (dev, id, fusion_id); + + case _IOC_NR(FUSION_REF_ZERO_TRYLOCK): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_ref_zero_trylock (dev, id, fusion_id); + + case _IOC_NR(FUSION_REF_UNLOCK): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_ref_zero_unlock (dev, id, fusion_id); + + case _IOC_NR(FUSION_REF_STAT): + if (get_user (id, (int*) arg)) + return -EFAULT; + + ret = fusion_ref_stat (dev, id, &refs); + if (ret) + return ret; + + return refs; + + case _IOC_NR(FUSION_REF_WATCH): + if (copy_from_user (&watch, (FusionRefWatch*) arg, sizeof(watch))) + return -EFAULT; + + return fusion_ref_watch (dev, watch.id, watch.call_id, watch.call_arg); + + case _IOC_NR(FUSION_REF_INHERIT): + if (copy_from_user (&inherit, (FusionRefInherit*) arg, sizeof(inherit))) + return -EFAULT; + + return fusion_ref_inherit (dev, inherit.id, inherit.from); + + case _IOC_NR(FUSION_REF_DESTROY): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_ref_destroy (dev, id); + } + + return -ENOSYS; +} + +static int +skirmish_ioctl (FusionDev *dev, Fusionee *fusionee, + unsigned int cmd, unsigned long arg) +{ + int id; + int ret; + int lock_count; + FusionID fusion_id = fusionee_id( fusionee ); + + switch (_IOC_NR(cmd)) { + case _IOC_NR(FUSION_SKIRMISH_NEW): + ret = fusion_skirmish_new (dev, &id); + if (ret) + return ret; + + if (put_user (id, (int*) arg)) { + fusion_skirmish_destroy (dev, id); + return -EFAULT; + } + return 0; + + case _IOC_NR(FUSION_SKIRMISH_PREVAIL): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_skirmish_prevail (dev, id, fusion_id); + + case _IOC_NR(FUSION_SKIRMISH_SWOOP): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_skirmish_swoop (dev, id, fusion_id); + + case _IOC_NR(FUSION_SKIRMISH_DISMISS): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_skirmish_dismiss (dev, id, fusion_id); + + case _IOC_NR(FUSION_SKIRMISH_DESTROY): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_skirmish_destroy (dev, id); + + case _IOC_NR(FUSION_SKIRMISH_LOCK_COUNT): + if (get_user (id, (int*) arg)) + return -EFAULT; + + ret = fusion_skirmish_lock_count (dev, id, fusion_id, &lock_count); + if (put_user(lock_count, ((int*)arg)+1)) + return -EFAULT; + + return ret; + } + + return -ENOSYS; +} + +static int +property_ioctl (FusionDev *dev, Fusionee *fusionee, + unsigned int cmd, unsigned long arg) +{ + int id; + int ret; + FusionID fusion_id = fusionee_id( fusionee ); + + switch (_IOC_NR(cmd)) { + case _IOC_NR(FUSION_PROPERTY_NEW): + ret = fusion_property_new (dev, &id); + if (ret) + return ret; + + if (put_user (id, (int*) arg)) { + fusion_property_destroy (dev, id); + return -EFAULT; + } + return 0; + + case _IOC_NR(FUSION_PROPERTY_LEASE): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_property_lease (dev, id, fusion_id); + + case _IOC_NR(FUSION_PROPERTY_PURCHASE): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_property_purchase (dev, id, fusion_id); + + case _IOC_NR(FUSION_PROPERTY_CEDE): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_property_cede (dev, id, fusion_id); + + case _IOC_NR(FUSION_PROPERTY_HOLDUP): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_property_holdup (dev, id, fusionee); + + case _IOC_NR(FUSION_PROPERTY_DESTROY): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_property_destroy (dev, id); + } + + return -ENOSYS; +} + +static int +reactor_ioctl (FusionDev *dev, Fusionee *fusionee, + unsigned int cmd, unsigned long arg) +{ + int id; + int ret; + FusionReactorDispatch dispatch; + FusionID fusion_id = fusionee_id( fusionee ); + + switch (_IOC_NR(cmd)) { + case _IOC_NR(FUSION_REACTOR_NEW): + ret = fusion_reactor_new (dev, &id); + if (ret) + return ret; + + if (put_user (id, (int*) arg)) { + fusion_reactor_destroy (dev, id); + return -EFAULT; + } + return 0; + + case _IOC_NR(FUSION_REACTOR_ATTACH): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_reactor_attach (dev, id, fusion_id); + + case _IOC_NR(FUSION_REACTOR_DETACH): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_reactor_detach (dev, id, fusion_id); + + case _IOC_NR(FUSION_REACTOR_DISPATCH): + if (copy_from_user (&dispatch, + (FusionReactorDispatch*) arg, sizeof(dispatch))) + return -EFAULT; + + if (dispatch.msg_size <= 0) + return -EINVAL; + + /* message data > 64k should be stored in shared memory */ + if (dispatch.msg_size > 0x10000) + return -EMSGSIZE; + + return fusion_reactor_dispatch (dev, dispatch.reactor_id, + dispatch.self ? NULL : fusionee, + dispatch.msg_size, dispatch.msg_data); + + case _IOC_NR(FUSION_REACTOR_DESTROY): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_reactor_destroy (dev, id); + } + + return -ENOSYS; +} + +static int +shmpool_ioctl (FusionDev *dev, Fusionee *fusionee, + unsigned int cmd, unsigned long arg) +{ + int id; + int ret; + FusionSHMPoolNew pool; + FusionSHMPoolAttach attach; + FusionSHMPoolDispatch dispatch; + FusionID fusion_id = fusionee_id( fusionee ); + + switch (_IOC_NR(cmd)) { + case _IOC_NR(FUSION_SHMPOOL_NEW): + if (copy_from_user (&pool, (FusionSHMPoolNew*) arg, sizeof(pool))) + return -EFAULT; + + ret = fusion_shmpool_new (dev, &pool); + if (ret) + return ret; + + if (copy_to_user ((FusionSHMPoolNew*) arg, &pool, sizeof(pool))) { + fusion_shmpool_destroy (dev, pool.pool_id); + return -EFAULT; + } + + return 0; + + case _IOC_NR(FUSION_SHMPOOL_ATTACH): + if (copy_from_user (&attach, + (FusionSHMPoolAttach*) arg, sizeof(attach))) + return -EFAULT; + + ret = fusion_shmpool_attach (dev, &attach, fusion_id); + if (ret) + return ret; + + if (copy_to_user ((FusionSHMPoolAttach*) arg, &attach, sizeof(attach))) { + fusion_shmpool_detach (dev, attach.pool_id, fusion_id); + return -EFAULT; + } + + return 0; + + case _IOC_NR(FUSION_SHMPOOL_DETACH): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_shmpool_detach (dev, id, fusion_id); + + case _IOC_NR(FUSION_SHMPOOL_DISPATCH): + if (copy_from_user (&dispatch, + (FusionSHMPoolDispatch*) arg, sizeof(dispatch))) + return -EFAULT; + + return fusion_shmpool_dispatch (dev, &dispatch, fusionee); + + case _IOC_NR(FUSION_SHMPOOL_DESTROY): + if (get_user (id, (int*) arg)) + return -EFAULT; + + return fusion_shmpool_destroy (dev, id); + } + + return -ENOSYS; +} + +static int +fusion_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + Fusionee *fusionee = file->private_data; + FusionDev *dev = fusion_devs[iminor(inode)]; + + DEBUG( "fusion_ioctl (0x%08x)\n", cmd ); + + switch (_IOC_TYPE(cmd)) { + case FT_LOUNGE: + return lounge_ioctl( file, dev, fusionee, cmd, arg ); + + case FT_MESSAGING: + return messaging_ioctl( dev, fusionee, cmd, arg ); + + case FT_CALL: + return call_ioctl( dev, fusionee, cmd, arg ); + + case FT_REF: + return ref_ioctl( dev, fusionee, cmd, arg ); + + case FT_SKIRMISH: + return skirmish_ioctl( dev, fusionee, cmd, arg ); + + case FT_PROPERTY: + return property_ioctl( dev, fusionee, cmd, arg ); + + case FT_REACTOR: + return reactor_ioctl( dev, fusionee, cmd, arg ); + + case FT_SHMPOOL: + return shmpool_ioctl( dev, fusionee, cmd, arg ); + } + + return -ENOSYS; +} + +static int +fusion_mmap( struct file *file, + struct vm_area_struct *vma ) +{ + Fusionee *fusionee = file->private_data; + FusionDev *dev = fusion_devs[iminor(file->f_dentry->d_inode)]; + unsigned int size; + + if (vma->vm_pgoff != 0) + return -EINVAL; + + size = vma->vm_end - vma->vm_start; + if (!size || size > PAGE_SIZE) + return -EINVAL; + + if (!dev->shared_area) { + if (fusionee_id( fusionee ) != FUSION_ID_MASTER) + return -EPERM; + + dev->shared_area = get_zeroed_page( GFP_KERNEL ); + if (!dev->shared_area) + return -ENOMEM; + + SetPageReserved( virt_to_page(dev->shared_area) ); + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + return remap_pfn_range( vma, vma->vm_start, + virt_to_phys((void*)dev->shared_area) >> PAGE_SHIFT, + PAGE_SIZE, vma->vm_page_prot ); +#else + return io_remap_page_range( vma->vm_start, + virt_to_phys((void*)dev->shared_area), + PAGE_SIZE, vma->vm_page_prot ); +#endif +} + +static struct file_operations fusion_fops = { + .owner = THIS_MODULE, + .open = fusion_open, + .flush = fusion_flush, + .release = fusion_release, + .read = fusion_read, + .poll = fusion_poll, + .ioctl = fusion_ioctl, + .mmap = fusion_mmap +}; + +/******************************************************************************/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +static int __init +register_devices(void) +{ + int i; + + if (register_chrdev (FUSION_MAJOR, "fusion", &fusion_fops)) { + printk (KERN_ERR "fusion: unable to get major %d\n", FUSION_MAJOR); + return -EIO; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 2) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) + fusion_class = class_create (THIS_MODULE, "fusion"); +#else + fusion_class = class_simple_create (THIS_MODULE, "fusion"); +#endif + if (IS_ERR(fusion_class)) { + unregister_chrdev (FUSION_MAJOR, "fusion"); + return PTR_ERR(fusion_class); + } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) + devfs_mk_dir("fusion"); +#endif + + for (i=0; i= KERNEL_VERSION(2, 6, 15) + class_device_create (fusion_class, + NULL, + MKDEV(FUSION_MAJOR, i), + NULL, "fusion%d", i); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) + class_device_create (fusion_class, + MKDEV(FUSION_MAJOR, i), + NULL, "fusion%d", i); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 2) + class_simple_device_add (fusion_class, + MKDEV(FUSION_MAJOR, i), + NULL, "fusion%d", i); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) + devfs_mk_cdev (MKDEV(FUSION_MAJOR, i), + S_IFCHR | S_IRUSR | S_IWUSR, + "fusion/%d", i); +#endif + } + + return 0; +} +#else +static int __init +register_devices(void) +{ + int i; + char buf[16]; + + if (devfs_register_chrdev (FUSION_MAJOR, "fusion", &fusion_fops)) { + printk (KERN_ERR "fusion: unable to get major %d\n", FUSION_MAJOR); + return -EIO; + } + + for (i=0; i= KERNEL_VERSION(2, 6, 0) +static void __exit +deregister_devices(void) +{ + int i; + + unregister_chrdev (FUSION_MAJOR, "fusion"); + + for (i=0; i= KERNEL_VERSION(2, 6, 2) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) + class_device_destroy (fusion_class, MKDEV(FUSION_MAJOR, i)); +#else + class_simple_device_remove (MKDEV(FUSION_MAJOR, i)); +#endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) + devfs_remove ("fusion/%d", i); +#endif + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 2) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) + class_destroy (fusion_class); +#else + class_simple_destroy (fusion_class); +#endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) + devfs_remove ("fusion"); +#endif +} +#else +static void __exit +deregister_devices(void) +{ + int i; + + devfs_unregister_chrdev (FUSION_MAJOR, "fusion"); + + for (i=0; i + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSIONDEV_H__ +#define __FUSIONDEV_H__ + +#include + +#include "entries.h" +#include "list.h" + +#define FUSION_ASSERT(exp) if (!(exp)) BUG() +#define FUSION_ASSUME(exp) if (!(exp)) printk( KERN_ERR "fusiondev: assumption '" #exp "' failed!\n" ) + +typedef struct { + int refs; + int index; + + struct semaphore enter_lock; + int enter_ok; + wait_queue_head_t enter_wait; + + unsigned long shared_area; + + struct proc_dir_entry *proc_dir; + + struct { + int property_lease_purchase; + int property_cede; + + int reactor_attach; + int reactor_detach; + + int ref_up; + int ref_down; + + int skirmish_prevail_swoop; + int skirmish_dismiss; + + int shmpool_attach; + int shmpool_detach; + } stat; + + struct { + int ids; + FusionLink *list; + struct semaphore lock; + } call; + + struct { + int last_id; + FusionLink *list; + struct semaphore lock; + wait_queue_head_t wait; + } fusionee; + + FusionEntries properties; + FusionEntries reactor; + FusionEntries ref; + FusionEntries shmpool; + FusionEntries skirmish; +} FusionDev; + +/* + * Special version of interruptible_sleep_on() that unlocks the mutex + * after adding the entry to the queue (just before schedule). + */ +void fusion_sleep_on (wait_queue_head_t *q, + struct semaphore *lock, + signed long *timeout_ms); + +#endif diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/fusionee.c linux-2.6.22.1/drivers/char/fusion/fusionee.c --- linux-2.6.22.1-0rig/drivers/char/fusion/fusionee.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/fusionee.c 2007-01-29 00:31:00.000000000 +0100 @@ -0,0 +1,584 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifdef HAVE_LINUX_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include + +#include + +#include "call.h" +#include "fifo.h" +#include "list.h" +#include "fusiondev.h" +#include "fusionee.h" +#include "property.h" +#include "reactor.h" +#include "ref.h" +#include "skirmish.h" +#include "shmpool.h" + +#if 0 +#define DEBUG(x...) printk (KERN_DEBUG "Fusion: " x) +#else +#define DEBUG(x...) do {} while (0) +#endif + +struct __Fusion_Fusionee { + FusionLink link; + + struct semaphore lock; + + FusionID id; + int pid; + + FusionFifo messages; + + int rcv_total; /* Total number of messages received. */ + int snd_total; /* Total number of messages sent. */ + + wait_queue_head_t wait; + + bool force_slave; +}; + +typedef struct { + FusionLink link; + + FusionMessageType type; + FusionID id; + int size; + void *data; +} Message; + +/******************************************************************************/ + +static int lookup_fusionee (FusionDev *dev, FusionID id, Fusionee **ret_fusionee); +static int lock_fusionee (FusionDev *dev, FusionID id, Fusionee **ret_fusionee); +static void unlock_fusionee (Fusionee *fusionee); + +/******************************************************************************/ + +static int +fusionees_read_proc(char *buf, char **start, off_t offset, + int len, int *eof, void *private) +{ + FusionLink *l; + FusionDev *dev = private; + int written = 0; + + if (down_interruptible (&dev->fusionee.lock)) + return -EINTR; + + fusion_list_foreach (l, dev->fusionee.list) { + Fusionee *fusionee = (Fusionee*) l; + + written += sprintf(buf+written, "(%5d) 0x%08lx (%4d messages waiting, %7d received, %7d sent)\n", + fusionee->pid, fusionee->id, fusionee->messages.count, fusionee->rcv_total, fusionee->snd_total); + if (written < offset) { + offset -= written; + written = 0; + } + + if (written >= len) + break; + } + + up (&dev->fusionee.lock); + + *start = buf + offset; + written -= offset; + if (written > len) { + *eof = 0; + return len; + } + + *eof = 1; + return(written<0) ? 0 : written; +} + +int +fusionee_init (FusionDev *dev) +{ + init_waitqueue_head (&dev->fusionee.wait); + + init_MUTEX (&dev->fusionee.lock); + + create_proc_read_entry("fusionees", 0, dev->proc_dir, + fusionees_read_proc, dev); + + return 0; +} + +void +fusionee_deinit (FusionDev *dev) +{ + FusionLink *l; + + down (&dev->fusionee.lock); + + remove_proc_entry ("fusionees", dev->proc_dir); + + l = dev->fusionee.list; + while (l) { + FusionLink *next = l->next; + Fusionee *fusionee = (Fusionee *) l; + + while (fusionee->messages.count) { + Message *message = (Message*) fusion_fifo_get (&fusionee->messages); + + kfree (message); + } + + kfree (fusionee); + + l = next; + } + + up (&dev->fusionee.lock); +} + +/******************************************************************************/ + +int +fusionee_new( FusionDev *dev, + bool force_slave, + Fusionee **ret_fusionee ) +{ + Fusionee *fusionee; + + fusionee = kmalloc (sizeof(Fusionee), GFP_KERNEL); + if (!fusionee) + return -ENOMEM; + + memset (fusionee, 0, sizeof(Fusionee)); + + if (down_interruptible (&dev->fusionee.lock)) { + kfree (fusionee); + return -EINTR; + } + + fusionee->pid = current->pid; + fusionee->force_slave = force_slave; + + init_MUTEX (&fusionee->lock); + + init_waitqueue_head (&fusionee->wait); + + fusion_list_prepend (&dev->fusionee.list, &fusionee->link); + + up (&dev->fusionee.lock); + + *ret_fusionee = fusionee; + + return 0; +} + +int +fusionee_enter( FusionDev *dev, + FusionEnter *enter, + Fusionee *fusionee ) +{ + if (enter->api.major != FUSION_API_MAJOR || enter->api.minor > FUSION_API_MINOR) + return -ENOPROTOOPT; + + if (down_interruptible( &dev->enter_lock )) + return -EINTR; + + if (dev->fusionee.last_id || fusionee->force_slave) { + while (!dev->enter_ok) { + fusion_sleep_on( &dev->enter_wait, &dev->enter_lock, NULL ); + + if (signal_pending(current)) + return -EINTR; + + if (down_interruptible( &dev->enter_lock )) + return -EINTR; + } + + FUSION_ASSERT( dev->fusionee.last_id != 0 ); + } + + fusionee->id = ++dev->fusionee.last_id; + + up( &dev->enter_lock ); + + enter->fusion_id = fusionee->id; + + return 0; +} + +int +fusionee_fork( FusionDev *dev, + FusionFork *fork, + Fusionee *fusionee ) +{ + int ret; + + ret = fusion_shmpool_fork_all( dev, fusionee->id, fork->fusion_id ); + if (ret) + return ret; + + ret = fusion_reactor_fork_all( dev, fusionee->id, fork->fusion_id ); + if (ret) + return ret; + + ret = fusion_ref_fork_all_local( dev, fusionee->id, fork->fusion_id ); + if (ret) + return ret; + + fork->fusion_id = fusionee->id; + + return 0; +} + +int +fusionee_send_message (FusionDev *dev, + Fusionee *sender, + FusionID recipient, + FusionMessageType msg_type, + int msg_id, + int msg_size, + const void *msg_data) +{ + int ret; + Message *message; + Fusionee *fusionee; + + DEBUG( "fusionee_send_message (%d -> %d, type %d, id %d, size %d)\n", + fusionee->id, recipient, msg_type, msg_id, msg_size ); + + ret = lookup_fusionee (dev, recipient, &fusionee); + if (ret) + return ret; + + if (down_interruptible (&fusionee->lock)) { + up (&dev->fusionee.lock); + return -EINTR; + } + + if (sender && sender != fusionee) { + if (down_interruptible (&sender->lock)) { + unlock_fusionee (fusionee); + up (&dev->fusionee.lock); + return -EINTR; + } + } + + up (&dev->fusionee.lock); + + + message = kmalloc (sizeof(Message) + msg_size, GFP_KERNEL); + if (!message) { + if (sender && sender != fusionee) + unlock_fusionee (sender); + unlock_fusionee (fusionee); + return -ENOMEM; + } + + message->data = message + 1; + + if (msg_type == FMT_CALL || msg_type == FMT_SHMPOOL) + memcpy (message->data, msg_data, msg_size); + else if (copy_from_user (message->data, msg_data, msg_size)) { + kfree (message); + if (sender && sender != fusionee) + unlock_fusionee (sender); + unlock_fusionee (fusionee); + return -EFAULT; + } + + message->type = msg_type; + message->id = msg_id; + message->size = msg_size; + + fusion_fifo_put (&fusionee->messages, &message->link); + + fusionee->rcv_total++; + if (sender) + sender->snd_total++; + + wake_up_interruptible_all (&fusionee->wait); + + if (sender && sender != fusionee) + unlock_fusionee (sender); + + unlock_fusionee (fusionee); + + return 0; +} + +int +fusionee_get_messages (FusionDev *dev, + Fusionee *fusionee, + void *buf, + int buf_size, + bool block) +{ + int written = 0; + + if (down_interruptible (&fusionee->lock)) + return -EINTR; + + while (!fusionee->messages.count) { + if (!block) { + unlock_fusionee (fusionee); + return -EAGAIN; + } + + fusion_sleep_on (&fusionee->wait, &fusionee->lock, 0); + + if (signal_pending(current)) + return -EINTR; + + if (down_interruptible (&fusionee->lock)) + return -EINTR; + } + + while (fusionee->messages.count) { + FusionReadMessage header; + Message *message = (Message*) fusionee->messages.first; + int bytes = message->size + sizeof(header); + + if (bytes > buf_size) { + if (!written) { + unlock_fusionee (fusionee); + return -EMSGSIZE; + } + + break; + } + + header.msg_type = message->type; + header.msg_id = message->id; + header.msg_size = message->size; + + if (copy_to_user (buf, &header, sizeof(header)) || + copy_to_user (buf + sizeof(header), message->data, message->size)) { + unlock_fusionee (fusionee); + return -EFAULT; + } + + written += bytes; + buf += bytes; + buf_size -= bytes; + + fusion_fifo_get (&fusionee->messages); + + kfree (message); + } + + unlock_fusionee (fusionee); + + return written; +} + +unsigned int +fusionee_poll (FusionDev *dev, + Fusionee *fusionee, + struct file *file, + poll_table *wait) +{ + int ret; + FusionID id = fusionee->id; + + poll_wait (file, &fusionee->wait, wait); + + + ret = lock_fusionee (dev, id, &fusionee); + if (ret) + return POLLERR; + + if (fusionee->messages.count) { + unlock_fusionee (fusionee); + + return POLLIN | POLLRDNORM; + } + + unlock_fusionee (fusionee); + + return 0; +} + +int +fusionee_kill (FusionDev *dev, + Fusionee *fusionee, + FusionID target, + int signal, + int timeout_ms) +{ + long timeout = -1; + + while (true) { + FusionLink *l; + int killed = 0; + + if (down_interruptible (&dev->fusionee.lock)) + return -EINTR; + + fusion_list_foreach (l, dev->fusionee.list) { + Fusionee *f = (Fusionee*) l; + + if (f != fusionee && (!target || target == f->id)) { + kill_proc (f->pid, signal, 0); + killed++; + } + } + + if (!killed || timeout_ms < 0) { + up (&dev->fusionee.lock); + break; + } + + if (timeout_ms) { + switch (timeout) { + case 0: /* timed out */ + up (&dev->fusionee.lock); + return -ETIMEDOUT; + + case -1: /* setup timeout */ + timeout = (timeout_ms * HZ + 500) / 1000; + if (!timeout) + timeout = 1; + + /* fall through */ + + default: + fusion_sleep_on (&dev->fusionee.wait, + &dev->fusionee.lock, &timeout); + break; + } + } + else + fusion_sleep_on (&dev->fusionee.wait, &dev->fusionee.lock, NULL); + + if (signal_pending(current)) + return -EINTR; + } + + return 0; +} + +void +fusionee_destroy (FusionDev *dev, + Fusionee *fusionee) +{ + /* Lock list. */ + down (&dev->fusionee.lock); + + /* Lock fusionee. */ + down (&fusionee->lock); + + /* Remove from list. */ + fusion_list_remove (&dev->fusionee.list, &fusionee->link); + + /* Wake up waiting killer. */ + wake_up_interruptible_all (&dev->fusionee.wait); + + /* Unlock list. */ + up (&dev->fusionee.lock); + + + /* Release locks, references, ... */ + fusion_call_destroy_all (dev, fusionee->id); + fusion_skirmish_dismiss_all (dev, fusionee->id); + fusion_reactor_detach_all (dev, fusionee->id); + fusion_property_cede_all (dev, fusionee->id); + fusion_ref_clear_all_local (dev, fusionee->id); + fusion_shmpool_detach_all (dev, fusionee->id); + + /* Free all pending messages. */ + while (fusionee->messages.count) { + Message *message = (Message*) fusion_fifo_get (&fusionee->messages); + + kfree (message); + } + + /* Unlock fusionee. */ + up (&fusionee->lock); + + + /* Free fusionee data. */ + kfree (fusionee); +} + +FusionID +fusionee_id( const Fusionee *fusionee ) +{ + return fusionee->id; +} + +/******************************************************************************/ + +static int +lookup_fusionee (FusionDev *dev, + FusionID id, + Fusionee **ret_fusionee) +{ + FusionLink *l; + + if (down_interruptible (&dev->fusionee.lock)) + return -EINTR; + + fusion_list_foreach (l, dev->fusionee.list) { + Fusionee *fusionee = (Fusionee *) l; + + if (fusionee->id == id) { + *ret_fusionee = fusionee; + return 0; + } + } + + up (&dev->fusionee.lock); + + return -EINVAL; +} + +static int +lock_fusionee (FusionDev *dev, + FusionID id, + Fusionee **ret_fusionee) +{ + int ret; + Fusionee *fusionee; + + ret = lookup_fusionee (dev, id, &fusionee); + if (ret) + return ret; + + fusion_list_move_to_front (&dev->fusionee.list, &fusionee->link); + + if (down_interruptible (&fusionee->lock)) { + up (&dev->fusionee.lock); + return -EINTR; + } + + up (&dev->fusionee.lock); + + *ret_fusionee = fusionee; + + return 0; +} + +static void +unlock_fusionee (Fusionee *fusionee) +{ + up (&fusionee->lock); +} + diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/fusionee.h linux-2.6.22.1/drivers/char/fusion/fusionee.h --- linux-2.6.22.1-0rig/drivers/char/fusion/fusionee.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/fusionee.h 2007-01-23 22:19:25.000000000 +0100 @@ -0,0 +1,75 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSIONEE_H__ +#define __FUSIONEE_H__ + +#include +#include + +#include "fusiondev.h" +#include "types.h" + +/* module init/cleanup */ + +int fusionee_init (FusionDev *dev); +void fusionee_deinit (FusionDev *dev); + + +/* internal functions */ + +int fusionee_new (FusionDev *dev, + bool force_slave, + Fusionee **ret_fusionee); + +int fusionee_enter (FusionDev *dev, + FusionEnter *enter, + Fusionee *fusionee); + +int fusionee_fork (FusionDev *dev, + FusionFork *fork, + Fusionee *fusionee); + +int fusionee_send_message (FusionDev *dev, + Fusionee *fusionee, + FusionID recipient, + FusionMessageType msg_type, + int msg_id, + int msg_size, + const void *msg_data); + +int fusionee_get_messages (FusionDev *dev, + Fusionee *fusionee, + void *buf, + int buf_size, + bool block); + +unsigned +int fusionee_poll (FusionDev *dev, + Fusionee *fusionee, + struct file *file, + poll_table *wait); + +int fusionee_kill (FusionDev *dev, + Fusionee *fusionee, + FusionID target, + int signal, + int timeout_ms); + +void fusionee_destroy (FusionDev *dev, + Fusionee *fusionee); + +FusionID fusionee_id( const Fusionee *fusionee ); + +#endif diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/Kconfig linux-2.6.22.1/drivers/char/fusion/Kconfig --- linux-2.6.22.1-0rig/drivers/char/fusion/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/Kconfig 2007-08-12 19:33:38.000000000 +0200 @@ -0,0 +1,8 @@ +config FUSION_DEVICE + tristate "Fusion device for DirectFB" + default m + ---help--- + The fusion device is a software device allowing the DirectFB + (Direct Frame Buffer) to support multiple applications + It is safe to say N unless you need to run several DirectFB + applications concurrently. diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/list.c linux-2.6.22.1/drivers/char/fusion/list.c --- linux-2.6.22.1-0rig/drivers/char/fusion/list.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/list.c 2003-06-16 19:47:03.000000000 +0200 @@ -0,0 +1,62 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +#include "list.h" + +void +fusion_list_prepend (FusionLink **list, FusionLink *link) +{ + link->prev = NULL; + link->next = *list; + + if (*list) + (*list)->prev = link; + + *list = link; +} + +void +fusion_list_remove (FusionLink **list, FusionLink *link) +{ + if (link->prev) + link->prev->next = link->next; + else + *list = link->next; + + if (link->next) + link->next->prev = link->prev; + + link->next = link->prev = NULL; +} + +void +fusion_list_move_to_front (FusionLink **list, FusionLink *link) +{ + if (*list == link) + return; + + link->prev->next = link->next; + + if (link->next) + link->next->prev = link->prev; + + link->prev = NULL; + link->next = *list; + + (*list)->prev = link; + + *list = link; +} diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/list.h linux-2.6.22.1/drivers/char/fusion/list.h --- linux-2.6.22.1-0rig/drivers/char/fusion/list.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/list.h 2004-08-17 19:24:36.000000000 +0200 @@ -0,0 +1,39 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSION__LIST_H__ +#define __FUSION__LIST_H__ + +typedef struct _FusionLink { + struct _FusionLink *next; + struct _FusionLink *prev; +} FusionLink; + +void fusion_list_prepend (FusionLink **list, FusionLink *link); +void fusion_list_remove (FusionLink **list, FusionLink *link); +void fusion_list_move_to_front (FusionLink **list, FusionLink *link); + + +#define fusion_list_foreach(elem, list) \ + for (elem = (void*)(list); \ + elem; \ + elem = (void*)(((FusionLink*)(elem))->next)) + +#define fusion_list_foreach_safe(elem, temp, list) \ + for (elem = (void*)(list), temp = ((elem) ? (void*)(((FusionLink*)(elem))->next) : NULL); \ + elem; \ + elem = (void*)(temp), temp = ((elem) ? (void*)(((FusionLink*)(elem))->next) : NULL)) + +#endif /* __FUSION__LIST_H__ */ + diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/Makefile linux-2.6.22.1/drivers/char/fusion/Makefile --- linux-2.6.22.1-0rig/drivers/char/fusion/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/Makefile 2005-10-29 02:38:05.000000000 +0200 @@ -0,0 +1,3 @@ +obj-$(CONFIG_FUSION_DEVICE) += fusion.o + +fusion-y := call.o entries.o fifo.o fusiondev.o fusionee.o list.o property.o reactor.o ref.o skirmish.o shmpool.o diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/property.c linux-2.6.22.1/drivers/char/fusion/property.c --- linux-2.6.22.1-0rig/drivers/char/fusion/property.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/property.c 2007-01-20 05:03:01.000000000 +0100 @@ -0,0 +1,340 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifdef HAVE_LINUX_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include + +#ifndef yield +#define yield schedule +#endif + +#include + +#include "entries.h" +#include "fusiondev.h" +#include "fusionee.h" +#include "list.h" +#include "property.h" + +typedef enum { + FUSION_PROPERTY_AVAILABLE = 0, + FUSION_PROPERTY_LEASED, + FUSION_PROPERTY_PURCHASED +} FusionPropertyState; + +typedef struct { + FusionEntry entry; + + FusionPropertyState state; + int fusion_id; /* non-zero if leased/purchased */ + unsigned long purchase_stamp; + int lock_pid; + int count; /* lock counter */ +} FusionProperty; + +static int +fusion_property_print( FusionEntry *entry, + void *ctx, + char *buf ) +{ + FusionProperty *property = (FusionProperty*) entry; + + if (property->state != FUSION_PROPERTY_AVAILABLE) { + return sprintf( buf, "%s by 0x%08x (%d) %dx\n", + property->state == FUSION_PROPERTY_LEASED ? "leased" : "purchased", + property->fusion_id, property->lock_pid, property->count ); + } + + return sprintf( buf, "\n" ); +} + +FUSION_ENTRY_CLASS( FusionProperty, property, NULL, NULL, fusion_property_print ) + +/******************************************************************************/ + +int +fusion_property_init( FusionDev *dev ) +{ + fusion_entries_init( &dev->properties, &property_class, dev ); + + create_proc_read_entry( "properties", 0, dev->proc_dir, + fusion_entries_read_proc, &dev->properties ); + + return 0; +} + +void +fusion_property_deinit( FusionDev *dev ) +{ + remove_proc_entry( "properties", dev->proc_dir ); + + fusion_entries_deinit( &dev->properties ); +} + +/******************************************************************************/ + +int +fusion_property_new( FusionDev *dev, int *ret_id ) +{ + return fusion_entry_create( &dev->properties, ret_id, NULL ); +} + +int +fusion_property_lease( FusionDev *dev, int id, int fusion_id ) +{ + int ret; + FusionProperty *property; + long timeout = -1; + + dev->stat.property_lease_purchase++; + + ret = fusion_property_lock( &dev->properties, id, false, &property ); + if (ret) + return ret; + + while (true) { + switch (property->state) { + case FUSION_PROPERTY_AVAILABLE: + property->state = FUSION_PROPERTY_LEASED; + property->fusion_id = fusion_id; + property->lock_pid = current->pid; + property->count = 1; + + fusion_property_unlock( property ); + return 0; + + case FUSION_PROPERTY_LEASED: + if (property->lock_pid == current->pid) { + property->count++; + + fusion_property_unlock( property ); + return 0; + } + + ret = fusion_property_wait( property, NULL ); + if (ret) + return ret; + + break; + + case FUSION_PROPERTY_PURCHASED: + if (property->lock_pid == current->pid) { + fusion_property_unlock( property ); + return -EIO; + } + + if (timeout == -1) { + if (jiffies - property->purchase_stamp > HZ / 10) { + fusion_property_unlock( property ); + return -EAGAIN; + } + + timeout = HZ / 10; + } + + ret = fusion_property_wait( property, &timeout ); + if (ret) + return ret; + + break; + + default: + BUG(); + } + } + + BUG(); + + /* won't reach this */ + return -1; +} + +int +fusion_property_purchase( FusionDev *dev, int id, int fusion_id ) +{ + int ret; + FusionProperty *property; + signed long timeout = -1; + + dev->stat.property_lease_purchase++; + + ret = fusion_property_lock( &dev->properties, id, false, &property ); + if (ret) + return ret; + + while (true) { + switch (property->state) { + case FUSION_PROPERTY_AVAILABLE: + property->state = FUSION_PROPERTY_PURCHASED; + property->fusion_id = fusion_id; + property->purchase_stamp = jiffies; + property->lock_pid = current->pid; + property->count = 1; + + fusion_property_notify( property, true ); + + fusion_property_unlock( property ); + return 0; + + case FUSION_PROPERTY_LEASED: + if (property->lock_pid == current->pid) { + fusion_property_unlock( property ); + return -EIO; + } + + ret = fusion_property_wait( property, NULL ); + if (ret) + return ret; + + break; + + case FUSION_PROPERTY_PURCHASED: + if (property->lock_pid == current->pid) { + property->count++; + + fusion_property_unlock( property ); + return 0; + } + + if (timeout == -1) { + if (jiffies - property->purchase_stamp > HZ) { + fusion_property_unlock( property ); + return -EAGAIN; + } + + timeout = HZ; + } + + ret = fusion_property_wait( property, &timeout ); + if (ret) + return ret; + + break; + + default: + BUG(); + } + } + + BUG(); + + /* won't reach this */ + return -1; +} + +int +fusion_property_cede( FusionDev *dev, int id, int fusion_id ) +{ + int ret; + FusionProperty *property; + bool purchased; + + dev->stat.property_cede++; + + ret = fusion_property_lock( &dev->properties, id, false, &property ); + if (ret) + return ret; + + if (property->lock_pid != current->pid) { + fusion_property_unlock( property ); + return -EIO; + } + + if (--property->count) { + fusion_property_unlock( property ); + return 0; + } + + purchased = (property->state == FUSION_PROPERTY_PURCHASED); + + property->state = FUSION_PROPERTY_AVAILABLE; + property->fusion_id = 0; + property->lock_pid = 0; + + fusion_property_notify( property, true ); + + fusion_property_unlock( property ); + + if (purchased) + yield(); + + return 0; +} + +int +fusion_property_holdup( FusionDev *dev, int id, Fusionee *fusionee ) +{ + int ret; + FusionProperty *property; + FusionID fusion_id = fusionee_id( fusionee ); + + if (fusion_id > 1) + return -EPERM; + + ret = fusion_property_lock( &dev->properties, id, false, &property ); + if (ret) + return ret; + + if (property->state == FUSION_PROPERTY_PURCHASED) { + if (property->fusion_id == fusion_id) { + fusion_property_unlock( property ); + return -EIO; + } + + fusionee_kill( dev, fusionee, property->fusion_id, SIGKILL, -1 ); + } + + fusion_property_unlock( property ); + + return 0; +} + +int +fusion_property_destroy( FusionDev *dev, int id ) +{ + return fusion_entry_destroy( &dev->properties, id ); +} + +void +fusion_property_cede_all( FusionDev *dev, int fusion_id ) +{ + FusionLink *l; + + down( &dev->properties.lock ); + + fusion_list_foreach (l, dev->properties.list) { + FusionProperty *property = (FusionProperty *) l; + + down( &property->entry.lock ); + + if (property->fusion_id == fusion_id) { + property->state = FUSION_PROPERTY_AVAILABLE; + property->fusion_id = 0; + property->lock_pid = 0; + + wake_up_interruptible_all (&property->entry.wait); + } + + up( &property->entry.lock ); + } + + up( &dev->properties.lock ); +} + diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/property.h linux-2.6.22.1/drivers/char/fusion/property.h --- linux-2.6.22.1-0rig/drivers/char/fusion/property.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/property.h 2007-01-20 05:03:01.000000000 +0100 @@ -0,0 +1,58 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSION__PROPERTY_H__ +#define __FUSION__PROPERTY_H__ + +#include "fusiondev.h" +#include "types.h" + + +/* module init/cleanup */ + +int fusion_property_init (FusionDev *dev); +void fusion_property_deinit (FusionDev *dev); + + +/* public API */ + +int fusion_property_new (FusionDev *dev, + int *ret_id); + +int fusion_property_lease (FusionDev *dev, + int id, + int fusion_id); + +int fusion_property_purchase (FusionDev *dev, + int id, + int fusion_id); + +int fusion_property_cede (FusionDev *dev, + int id, + int fusion_id); + +int fusion_property_holdup (FusionDev *dev, + int id, + Fusionee *fusionee); + +int fusion_property_destroy (FusionDev *dev, + int id); + + +/* internal functions */ + +void fusion_property_cede_all (FusionDev *dev, + int fusion_id); + +#endif diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/reactor.c linux-2.6.22.1/drivers/char/fusion/reactor.c --- linux-2.6.22.1-0rig/drivers/char/fusion/reactor.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/reactor.c 2007-01-20 05:03:01.000000000 +0100 @@ -0,0 +1,367 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifdef HAVE_LINUX_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include + +#include + +#include "fusiondev.h" +#include "fusionee.h" +#include "list.h" +#include "reactor.h" + +typedef struct { + FusionLink link; + + int fusion_id; + + int count; /* number of attach calls */ +} ReactorNode; + +typedef struct { + FusionEntry entry; + + FusionLink *nodes; + + int dispatch_count; + + bool destroyed; +} FusionReactor; + +/******************************************************************************/ + +static int fork_node ( FusionReactor *reactor, + FusionID fusion_id, + FusionID from_id ); + +static void free_all_nodes( FusionReactor *reactor ); + +/******************************************************************************/ + +static inline ReactorNode * +get_node (FusionReactor *reactor, + FusionID fusion_id) +{ + ReactorNode *node; + + fusion_list_foreach (node, reactor->nodes) { + if (node->fusion_id == fusion_id) + return node; + } + + return NULL; +} + +/******************************************************************************/ + +static void +fusion_reactor_destruct( FusionEntry *entry, + void *ctx ) +{ + FusionReactor *reactor = (FusionReactor*) entry; + + free_all_nodes( reactor ); +} + +static int +fusion_reactor_print( FusionEntry *entry, + void *ctx, + char *buf ) +{ + int num = 0; + FusionReactor *reactor = (FusionReactor*) entry; + FusionLink *node = reactor->nodes; + + fusion_list_foreach (node, reactor->nodes) { + num++; + } + + return sprintf( buf, "%5dx dispatch, %d nodes%s\n", reactor->dispatch_count, num, + reactor->destroyed ? " DESTROYED" : "" ); +} + + +FUSION_ENTRY_CLASS( FusionReactor, reactor, NULL, + fusion_reactor_destruct, fusion_reactor_print ) + +/******************************************************************************/ + +int +fusion_reactor_init (FusionDev *dev) +{ + fusion_entries_init( &dev->reactor, &reactor_class, dev ); + + create_proc_read_entry( "reactors", 0, dev->proc_dir, + fusion_entries_read_proc, &dev->reactor ); + + return 0; +} + +void +fusion_reactor_deinit (FusionDev *dev) +{ + remove_proc_entry ("reactors", dev->proc_dir); + + fusion_entries_deinit( &dev->reactor ); +} + +/******************************************************************************/ + +int +fusion_reactor_new (FusionDev *dev, int *ret_id) +{ + return fusion_entry_create( &dev->reactor, ret_id, NULL ); +} + +int +fusion_reactor_attach (FusionDev *dev, int id, FusionID fusion_id) +{ + int ret; + ReactorNode *node; + FusionReactor *reactor; + + ret = fusion_reactor_lock( &dev->reactor, id, false, &reactor ); + if (ret) + return ret; + + if (reactor->destroyed) { + fusion_reactor_unlock( reactor ); + return -EIDRM; + } + + dev->stat.reactor_attach++; + + node = get_node (reactor, fusion_id); + if (!node) { + node = kmalloc (sizeof(ReactorNode), GFP_KERNEL); + if (!node) { + fusion_reactor_unlock( reactor ); + return -ENOMEM; + } + + node->fusion_id = fusion_id; + node->count = 1; + + fusion_list_prepend (&reactor->nodes, &node->link); + } + else + node->count++; + + fusion_reactor_unlock( reactor ); + + return 0; +} + +int +fusion_reactor_detach (FusionDev *dev, int id, FusionID fusion_id) +{ + int ret; + ReactorNode *node; + FusionReactor *reactor; + + ret = fusion_reactor_lock( &dev->reactor, id, true, &reactor ); + if (ret) + return ret; + + dev->stat.reactor_detach++; + + node = get_node (reactor, fusion_id); + if (!node) { + fusion_reactor_unlock( reactor ); + up( &dev->reactor.lock ); + return -EIO; + } + + if (! --node->count) { + fusion_list_remove (&reactor->nodes, &node->link); + kfree (node); + } + + if (reactor->destroyed && !reactor->nodes) + fusion_entry_destroy_locked( &dev->reactor, &reactor->entry ); + else + fusion_reactor_unlock( reactor ); + + up( &dev->reactor.lock ); + + return 0; +} + +int +fusion_reactor_dispatch (FusionDev *dev, int id, Fusionee *fusionee, + int msg_size, const void *msg_data) +{ + int ret; + FusionLink *l; + FusionReactor *reactor; + FusionID fusion_id = fusionee ? fusionee_id( fusionee ) : 0; + + ret = fusion_reactor_lock( &dev->reactor, id, false, &reactor ); + if (ret) + return ret; + + if (reactor->destroyed) { + fusion_reactor_unlock( reactor ); + return -EIDRM; + } + + reactor->dispatch_count++; + + fusion_list_foreach (l, reactor->nodes) { + ReactorNode *node = (ReactorNode *) l; + + if (node->fusion_id == fusion_id) + continue; + + fusionee_send_message (dev, fusionee, node->fusion_id, FMT_REACTOR, + reactor->entry.id, msg_size, msg_data); + } + + fusion_reactor_unlock( reactor ); + + return 0; +} + +int +fusion_reactor_destroy (FusionDev *dev, int id) +{ + int ret; + FusionReactor *reactor; + + ret = fusion_reactor_lock( &dev->reactor, id, true, &reactor ); + if (ret) + return ret; + + if (reactor->destroyed) { + fusion_reactor_unlock( reactor ); + up( &dev->reactor.lock ); + return -EIDRM; + } + + reactor->destroyed = true; + + if (!reactor->nodes) + fusion_entry_destroy_locked( &dev->reactor, &reactor->entry ); + else + fusion_reactor_unlock( reactor ); + + up( &dev->reactor.lock ); + + return 0; +} + +void +fusion_reactor_detach_all (FusionDev *dev, FusionID fusion_id) +{ + FusionLink *l, *n; + + down (&dev->reactor.lock); + + fusion_list_foreach_safe (l, n, dev->reactor.list) { + ReactorNode *node; + FusionReactor *reactor = (FusionReactor *) l; + + down (&reactor->entry.lock); + + fusion_list_foreach (node, reactor->nodes) { + if (node->fusion_id == fusion_id) { + fusion_list_remove (&reactor->nodes, &node->link); + kfree (node); + break; + } + } + + if (reactor->destroyed && !reactor->nodes) + fusion_entry_destroy_locked( &dev->reactor, &reactor->entry ); + else + up (&reactor->entry.lock); + } + + up (&dev->reactor.lock); +} + +int +fusion_reactor_fork_all (FusionDev *dev, FusionID fusion_id, FusionID from_id) +{ + FusionLink *l; + int ret = 0; + + down (&dev->reactor.lock); + + fusion_list_foreach (l, dev->reactor.list) { + FusionReactor *reactor = (FusionReactor *) l; + + ret = fork_node (reactor, fusion_id, from_id); + if (ret) + break; + } + + up (&dev->reactor.lock); + + return ret; +} + +/******************************************************************************/ + +static int +fork_node (FusionReactor *reactor, FusionID fusion_id, FusionID from_id) +{ + ReactorNode *node; + + down (&reactor->entry.lock); + + fusion_list_foreach (node, reactor->nodes) { + if (node->fusion_id == from_id) { + ReactorNode *new_node; + + new_node = kmalloc (sizeof(ReactorNode), GFP_KERNEL); + if (!new_node) { + up (&reactor->entry.lock); + return -ENOMEM; + } + + new_node->fusion_id = fusion_id; + new_node->count = node->count; + + fusion_list_prepend (&reactor->nodes, &new_node->link); + + break; + } + } + + up (&reactor->entry.lock); + + return 0; +} + +static void +free_all_nodes (FusionReactor *reactor) + +{ + FusionLink *n; + ReactorNode *node; + + fusion_list_foreach_safe (node, n, reactor->nodes) { + kfree (node); + } + + reactor->nodes = NULL; +} diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/reactor.h linux-2.6.22.1/drivers/char/fusion/reactor.h --- linux-2.6.22.1-0rig/drivers/char/fusion/reactor.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/reactor.h 2007-01-20 05:03:01.000000000 +0100 @@ -0,0 +1,59 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSION__REACTOR_H__ +#define __FUSION__REACTOR_H__ + +#include "fusiondev.h" +#include "types.h" + + +/* module init/cleanup */ + +int fusion_reactor_init (FusionDev *dev); +void fusion_reactor_deinit (FusionDev *dev); + + +/* public API */ + +int fusion_reactor_new (FusionDev *dev, + int *id); + +int fusion_reactor_attach (FusionDev *dev, + int id, + FusionID fusion_id); + +int fusion_reactor_detach (FusionDev *dev, + int id, + FusionID fusion_id); + +int fusion_reactor_dispatch (FusionDev *dev, + int id, + Fusionee *fusionee, + int msg_size, + const void *msg_data); + +int fusion_reactor_destroy (FusionDev *dev, + int id); + + +/* internal functions */ + +void fusion_reactor_detach_all (FusionDev *dev, + FusionID fusion_id); + +int fusion_reactor_fork_all (FusionDev *dev, + FusionID fusion_id, + FusionID from_id); +#endif diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/ref.c linux-2.6.22.1/drivers/char/fusion/ref.c --- linux-2.6.22.1-0rig/drivers/char/fusion/ref.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/ref.c 2007-01-06 14:09:45.000000000 +0100 @@ -0,0 +1,680 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifdef HAVE_LINUX_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include + +#include + +#include "fusiondev.h" +#include "list.h" +#include "call.h" +#include "ref.h" + +typedef struct __Fusion_FusionRef FusionRef; + +typedef struct { + FusionLink link; + FusionID fusion_id; + int refs; +} LocalRef; + +typedef struct { + FusionLink link; + FusionRef *ref; +} Inheritor; + +struct __Fusion_FusionRef { + FusionEntry entry; + + int global; + int local; + + int locked; /* non-zero fusion id of lock owner */ + + bool watched; /* true if watch has been installed */ + int call_id; /* id of call registered with a watch */ + int call_arg; /* optional call parameter */ + + FusionRef *inherited; + FusionLink *inheritors; + + FusionLink *local_refs; +}; + +/**********************************************************************************************************************/ + +static int add_local ( FusionRef *ref, FusionID fusion_id, int add ); +static void clear_local ( FusionDev *dev, FusionRef *ref, FusionID fusion_id ); +static int fork_local ( FusionDev *dev, FusionRef *ref, FusionID fusion_id, FusionID from_id ); +static void free_all_local ( FusionRef *ref ); + +static int propagate_local ( FusionDev *dev, FusionRef *ref, int diff ); + +static void notify_ref ( FusionDev *dev, FusionRef *ref ); + +static int add_inheritor ( FusionRef *ref, FusionRef *from ); +static void remove_inheritor( FusionRef *ref, FusionRef *from ); +static void drop_inheritors ( FusionDev *dev, FusionRef *ref ); + +/**********************************************************************************************************************/ + +static void +fusion_ref_destruct( FusionEntry *entry, + void *ctx ) +{ + FusionRef *ref = (FusionRef*) entry; + FusionDev *dev = (FusionDev*) ctx; + + drop_inheritors( dev, ref ); + + if (ref->inherited) + remove_inheritor( ref, ref->inherited ); + + free_all_local( ref ); +} + +static int +fusion_ref_print( FusionEntry *entry, + void *ctx, + char *buf ) +{ + FusionRef *ref = (FusionRef*) entry; + + if (ref->locked) + return sprintf( buf, "%2d %2d (locked by %d)\n", ref->global, ref->local, ref->locked ); + + return sprintf( buf, "%2d %2d\n", ref->global, ref->local ); +} + +FUSION_ENTRY_CLASS( FusionRef, ref, NULL, + fusion_ref_destruct, fusion_ref_print ); + +/**********************************************************************************************************************/ + +int +fusion_ref_init( FusionDev *dev ) +{ + fusion_entries_init( &dev->ref, &ref_class, dev ); + + create_proc_read_entry( "refs", 0, dev->proc_dir, fusion_entries_read_proc, &dev->ref ); + + return 0; +} + +void +fusion_ref_deinit( FusionDev *dev ) +{ + remove_proc_entry( "refs", dev->proc_dir ); + + fusion_entries_deinit( &dev->ref ); +} + +/**********************************************************************************************************************/ + +int +fusion_ref_new( FusionDev *dev, int *ret_id ) +{ + return fusion_entry_create( &dev->ref, ret_id, NULL ); +} + +int +fusion_ref_up (FusionDev *dev, int id, FusionID fusion_id) +{ + int ret; + FusionRef *ref; + + ret = fusion_ref_lock( &dev->ref, id, true, &ref ); + if (ret) + return ret; + + dev->stat.ref_up++; + + if (ref->locked) { + ret = -EAGAIN; + goto out; + } + + if (fusion_id) { + ret = add_local (ref, fusion_id, 1); + if (ret) + goto out; + + ret = propagate_local( dev, ref, 1 ); + } + else + ref->global++; + + +out: + fusion_ref_unlock( ref ); + up( &dev->ref.lock ); + + return ret; +} + +int +fusion_ref_down (FusionDev *dev, int id, FusionID fusion_id) +{ + int ret; + FusionRef *ref; + + ret = fusion_ref_lock( &dev->ref, id, true, &ref ); + if (ret) + return ret; + + dev->stat.ref_down++; + + if (ref->locked) { + ret = -EAGAIN; + goto out; + } + + if (fusion_id) { + ret = -EIO; + if (!ref->local) + goto out; + + ret = add_local (ref, fusion_id, -1); + if (ret) + goto out; + + ret = propagate_local( dev, ref, -1 ); + } + else { + if (!ref->global) { + ret = -EIO; + goto out; + } + + ref->global--; + + if (ref->local + ref->global == 0) + notify_ref (dev, ref); + } + + +out: + fusion_ref_unlock( ref ); + up( &dev->ref.lock ); + + return ret; +} + +int +fusion_ref_zero_lock (FusionDev *dev, int id, FusionID fusion_id) +{ + int ret; + FusionRef *ref; + + ret = fusion_ref_lock( &dev->ref, id, false, &ref ); + if (ret) + return ret; + + while (true) { + if (ref->watched) { + fusion_ref_unlock( ref ); + return -EACCES; + } + + if (ref->locked) { + fusion_ref_unlock( ref ); + return ref->locked == fusion_id ? -EIO : -EAGAIN; + } + + if (ref->global || ref->local) { + ret = fusion_ref_wait( ref, NULL ); + if (ret) + return ret; + } + else + break; + } + + ref->locked = fusion_id; + + fusion_ref_unlock( ref ); + + return 0; +} + +int +fusion_ref_zero_trylock (FusionDev *dev, int id, FusionID fusion_id) +{ + int ret; + FusionRef *ref; + + ret = fusion_ref_lock( &dev->ref, id, false, &ref ); + if (ret) + return ret; + + if (ref->locked) { + fusion_ref_unlock( ref ); + return ref->locked == fusion_id ? -EIO : -EAGAIN; + } + + if (ref->global || ref->local) + ret = -ETOOMANYREFS; + else + ref->locked = fusion_id; + + fusion_ref_unlock( ref ); + + return ret; +} + +int +fusion_ref_zero_unlock (FusionDev *dev, int id, FusionID fusion_id) +{ + int ret; + FusionRef *ref; + + ret = fusion_ref_lock( &dev->ref, id, false, &ref ); + if (ret) + return ret; + + if (ref->locked != fusion_id) { + fusion_ref_unlock( ref ); + return -EIO; + } + + ref->locked = 0; + + fusion_ref_unlock( ref ); + + return 0; +} + +int +fusion_ref_stat (FusionDev *dev, int id, int *refs) +{ + int ret; + FusionRef *ref; + + ret = fusion_ref_lock( &dev->ref, id, false, &ref ); + if (ret) + return ret; + + *refs = ref->global + ref->local; + + fusion_ref_unlock( ref ); + + return 0; +} + +int +fusion_ref_watch (FusionDev *dev, + int id, + int call_id, + int call_arg) +{ + int ret; + FusionRef *ref; + + ret = fusion_ref_lock( &dev->ref, id, false, &ref ); + if (ret) + return ret; + + if (ref->entry.pid != current->pid) { + fusion_ref_unlock( ref ); + return -EACCES; + } + + if (ref->global + ref->local == 0) { + fusion_ref_unlock( ref ); + return -EIO; + } + + if (ref->watched) { + fusion_ref_unlock( ref ); + return -EBUSY; + } + + ref->watched = true; + ref->call_id = call_id; + ref->call_arg = call_arg; + + fusion_ref_notify( ref, true ); + + fusion_ref_unlock( ref ); + + return 0; +} + +int +fusion_ref_inherit (FusionDev *dev, + int id, + int from_id) +{ + int ret; + FusionRef *ref; + FusionRef *from = NULL; + + ret = fusion_ref_lock( &dev->ref, id, true, &ref ); + if (ret) + return ret; + + ret = -EBUSY; + if (ref->inherited) + goto out; + + ret = -EINVAL; + fusion_list_foreach (from, dev->ref.list) { + if (from->entry.id == from_id) { + if (down_interruptible( &from->entry.lock )) { + ret = -EINTR; + from = NULL; + } + + break; + } + } + if (!from) + goto out; + + ret = add_inheritor( ref, from ); + if (ret) + goto out; + + ret = propagate_local( dev, ref, from->local ); + if (ret) + goto out; + + ref->inherited = from; + +out: + if (from) + up( &from->entry.lock ); + + fusion_ref_unlock( ref ); + up ( &dev->ref.lock ); + + return ret; +} + +int +fusion_ref_destroy (FusionDev *dev, int id) +{ + return fusion_entry_destroy( &dev->ref, id ); +} + +void +fusion_ref_clear_all_local( FusionDev *dev, FusionID fusion_id ) +{ + FusionRef *ref; + + down( &dev->ref.lock ); + + fusion_list_foreach (ref, dev->ref.list) + clear_local( dev, ref, fusion_id ); + + up( &dev->ref.lock ); +} + +int +fusion_ref_fork_all_local( FusionDev *dev, FusionID fusion_id, FusionID from_id ) +{ + FusionRef *ref; + int ret = 0; + + down( &dev->ref.lock ); + + fusion_list_foreach (ref, dev->ref.list) { + ret = fork_local( dev, ref, fusion_id, from_id ); + if (ret) + break; + } + + up( &dev->ref.lock ); + + return ret; +} + +/**********************************************************************************************************************/ + +static int +add_local (FusionRef *ref, FusionID fusion_id, int add) +{ + FusionLink *l; + LocalRef *local; + + fusion_list_foreach (l, ref->local_refs) { + local = (LocalRef *) l; + + if (local->fusion_id == fusion_id) { + fusion_list_move_to_front( &ref->local_refs, l ); + + if (local->refs + add < 0) + return -EIO; + + local->refs += add; + return 0; + } + } + + /* Can only create local node if value is positive. */ + if (add <= 0) + return -EIO; + + local = kmalloc (sizeof(LocalRef), GFP_KERNEL); + if (!local) + return -ENOMEM; + + local->fusion_id = fusion_id; + local->refs = add; + + fusion_list_prepend (&ref->local_refs, &local->link); + + return 0; +} + +static void +clear_local (FusionDev *dev, FusionRef *ref, FusionID fusion_id) +{ + FusionLink *l; + + down (&ref->entry.lock); + + if (ref->locked == fusion_id) { + ref->locked = 0; + wake_up_interruptible_all (&ref->entry.wait); + } + + fusion_list_foreach (l, ref->local_refs) { + LocalRef *local = (LocalRef *) l; + + if (local->fusion_id == fusion_id) { + if (local->refs) + propagate_local( dev, ref, - local->refs ); + + fusion_list_remove( &ref->local_refs, l ); + + kfree (l); + break; + } + } + + up (&ref->entry.lock); +} + +static int +fork_local (FusionDev *dev, FusionRef *ref, FusionID fusion_id, FusionID from_id) +{ + FusionLink *l; + int ret = 0; + + down (&ref->entry.lock); + + fusion_list_foreach (l, ref->local_refs) { + LocalRef *local = (LocalRef *) l; + + if (local->fusion_id == from_id) { + if (local->refs) { + LocalRef *new_local; + + new_local = kmalloc (sizeof(LocalRef), GFP_KERNEL); + if (!new_local) { + ret = -ENOMEM; + break; + } + + new_local->fusion_id = fusion_id; + new_local->refs = local->refs; + + fusion_list_prepend( &ref->local_refs, &new_local->link ); + + propagate_local( dev, ref, local->refs ); + } + break; + } + } + + up (&ref->entry.lock); + + return ret; +} + +static void +free_all_local (FusionRef *ref) +{ + FusionLink *l = ref->local_refs; + + while (l) { + FusionLink *next = l->next; + + kfree (l); + + l = next; + } + + ref->local_refs = NULL; +} + +static void +notify_ref (FusionDev *dev, FusionRef *ref) +{ + if (ref->watched) { + FusionCallExecute execute; + + execute.call_id = ref->call_id; + execute.call_arg = ref->call_arg; + execute.call_ptr = NULL; + + fusion_call_execute (dev, 0, &execute); + } + else + wake_up_interruptible_all (&ref->entry.wait); +} + +static int +propagate_local( FusionDev *dev, FusionRef *ref, int diff ) +{ + FusionLink *l; + + /* Recurse into inheritors. */ + fusion_list_foreach (l, ref->inheritors) { + FusionRef *inheritor = ((Inheritor*) l)->ref; + + if (down_interruptible( &inheritor->entry.lock )) { + printk( KERN_ERR "fusion_ref: propagate_local() interrupted!\n" ); + //return -EINTR; + } + + propagate_local( dev, inheritor, diff ); + + up( &inheritor->entry.lock ); + } + + /* Apply difference. */ + ref->local += diff; + + /* Notify zero count. */ + if (ref->local + ref->global == 0) + notify_ref( dev, ref ); + + return 0; +} + +static int +add_inheritor(FusionRef *ref, FusionRef *from) +{ + Inheritor *inheritor; + + inheritor = kmalloc (sizeof(Inheritor), GFP_KERNEL); + if (!inheritor) + return -ENOMEM; + + inheritor->ref = ref; + + fusion_list_prepend( &from->inheritors, &inheritor->link ); + + return 0; +} + +static void +remove_inheritor(FusionRef *ref, FusionRef *from) +{ + FusionLink *l; + + down( &from->entry.lock ); + + fusion_list_foreach (l, from->inheritors) { + Inheritor *inheritor = (Inheritor*) l; + + if (inheritor->ref == ref) { + fusion_list_remove( &from->inheritors, &inheritor->link ); + + kfree( l ); + break; + } + } + + up( &from->entry.lock ); +} + +static void +drop_inheritors( FusionDev *dev, FusionRef *ref ) +{ + FusionLink *l = ref->inheritors; + + while (l) { + FusionLink *next = l->next; + FusionRef *inheritor = ((Inheritor*) l)->ref; + + if (down_interruptible( &inheritor->entry.lock )) { + printk( KERN_ERR "fusion_ref: drop_inheritors() interrupted!\n" ); + //return; + } + + propagate_local( dev, inheritor, - ref->local ); + + inheritor->inherited = NULL; + + up( &inheritor->entry.lock ); + + + kfree (l); + + l = next; + } + + ref->inheritors = NULL; +} + diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/ref.h linux-2.6.22.1/drivers/char/fusion/ref.h --- linux-2.6.22.1-0rig/drivers/char/fusion/ref.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/ref.h 2006-08-14 11:16:54.000000000 +0200 @@ -0,0 +1,79 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSION__REF_H__ +#define __FUSION__REF_H__ + +#include "fusiondev.h" +#include "types.h" + + +/* module init/cleanup */ + +int fusion_ref_init (FusionDev *dev); +void fusion_ref_deinit (FusionDev *dev); + + +/* public API */ + +int fusion_ref_new (FusionDev *dev, + int *id); + +int fusion_ref_up (FusionDev *dev, + int id, + FusionID fusion_id); + +int fusion_ref_down (FusionDev *dev, + int id, + FusionID fusion_id); + +int fusion_ref_zero_lock (FusionDev *dev, + int id, + FusionID fusion_id); + +int fusion_ref_zero_trylock (FusionDev *dev, + int id, + FusionID fusion_id); + +int fusion_ref_zero_unlock (FusionDev *dev, + int id, + FusionID fusion_id); + +int fusion_ref_stat (FusionDev *dev, + int id, + int *refs); + +int fusion_ref_watch (FusionDev *dev, + int id, + int call_id, + int call_arg); + +int fusion_ref_inherit (FusionDev *dev, + int id, + int from); + +int fusion_ref_destroy (FusionDev *dev, + int id); + + +/* internal functions */ + +void fusion_ref_clear_all_local (FusionDev *dev, + FusionID fusion_id); + +int fusion_ref_fork_all_local (FusionDev *dev, + FusionID fusion_id, + FusionID from_id); + +#endif diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/shmpool.c linux-2.6.22.1/drivers/char/fusion/shmpool.c --- linux-2.6.22.1-0rig/drivers/char/fusion/shmpool.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/shmpool.c 2007-01-20 05:03:01.000000000 +0100 @@ -0,0 +1,444 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifdef HAVE_LINUX_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include + +#include + +#include "fusiondev.h" +#include "fusionee.h" +#include "list.h" +#include "shmpool.h" + + +#define SHM_BASE 0x20010000 /* virtual base address */ +#define SHM_SIZE 0x1FFEF000 /* size of virtual address space */ + + +typedef struct { + FusionLink link; + unsigned long next_base; +} AddrEntry; + + +typedef struct { + FusionLink link; + + FusionID fusion_id; + + int count; /* number of attach calls */ +} SHMPoolNode; + +typedef struct { + FusionEntry entry; + + int max_size; + + void *addr_base; + int size; + + AddrEntry *addr_entry; + + FusionLink *nodes; + + int dispatch_count; +} FusionSHMPool; + +/******************************************************************************/ + +static SHMPoolNode *get_node ( FusionSHMPool *shmpool, + FusionID fusion_id ); + +static void remove_node ( FusionSHMPool *shmpool, + FusionID fusion_id ); + +static int fork_node ( FusionSHMPool *shmpool, + FusionID fusion_id, + FusionID from_id ); + +static void free_all_nodes( FusionSHMPool *shmpool ); + +/******************************************************************************/ + + +static DECLARE_MUTEX (addr_lock); +static FusionLink *addr_entries; +static unsigned long addr_base = SHM_BASE; + +/******************************************************************************/ + +static AddrEntry * +add_addr_entry( unsigned long next_base ) +{ + AddrEntry *entry = kmalloc( sizeof(AddrEntry), GFP_KERNEL ); + + entry->next_base = next_base; + + fusion_list_prepend( &addr_entries, &entry->link ); + + return entry; +} + +/******************************************************************************/ + +static int +fusion_shmpool_construct( FusionEntry *entry, + void *ctx, + void *create_ctx ) +{ + FusionSHMPool *shmpool = (FusionSHMPool*) entry; + FusionSHMPoolNew *poolnew = create_ctx; + + down( &addr_lock ); + + if (addr_base + poolnew->max_size >= SHM_BASE + SHM_SIZE) { + up( &addr_lock ); + printk( KERN_WARNING "%s: virtual address space exhausted! (FIXME)\n", __FUNCTION__ ); + return -ENOSPC; + } + + shmpool->max_size = poolnew->max_size; + shmpool->addr_base = poolnew->addr_base = (void*) addr_base; + + addr_base += PAGE_ALIGN(poolnew->max_size) + PAGE_SIZE; /* fence page */ + + shmpool->addr_entry = add_addr_entry( addr_base ); + + up( &addr_lock ); + + return 0; +} + +static void +fusion_shmpool_destruct( FusionEntry *entry, + void *ctx ) +{ + AddrEntry *addr_entry; + FusionSHMPool *shmpool = (FusionSHMPool*) entry; + + free_all_nodes( shmpool ); + + + down( &addr_lock ); + + fusion_list_remove( &addr_entries, &shmpool->addr_entry->link ); + + + /* + * free trailing address space + */ + + addr_base = SHM_BASE; + + fusion_list_foreach (addr_entry, addr_entries) { + if (addr_entry->next_base > addr_base) + addr_base = addr_entry->next_base; + } + + up( &addr_lock ); +} + +static int +fusion_shmpool_print( FusionEntry *entry, + void *ctx, + char *buf ) +{ + int num = 0; + FusionSHMPool *shmpool = (FusionSHMPool*) entry; + FusionLink *node = shmpool->nodes; + + fusion_list_foreach (node, shmpool->nodes) { + num++; + } + + return sprintf( buf, "0x%p [0x%x] - 0x%x, %dx dispatch, %d nodes\n", + shmpool->addr_base, shmpool->max_size, shmpool->size, + shmpool->dispatch_count, num ); +} + + +FUSION_ENTRY_CLASS( FusionSHMPool, shmpool, fusion_shmpool_construct, + fusion_shmpool_destruct, fusion_shmpool_print ) + +/******************************************************************************/ + +int +fusion_shmpool_init (FusionDev *dev) +{ + fusion_entries_init( &dev->shmpool, &shmpool_class, dev ); + + create_proc_read_entry( "shmpools", 0, dev->proc_dir, + fusion_entries_read_proc, &dev->shmpool ); + + return 0; +} + +void +fusion_shmpool_deinit (FusionDev *dev) +{ + remove_proc_entry ("shmpools", dev->proc_dir); + + fusion_entries_deinit( &dev->shmpool ); +} + +/******************************************************************************/ + +int +fusion_shmpool_new (FusionDev *dev, + FusionSHMPoolNew *pool) +{ + if (pool->max_size <= 0) + return -EINVAL; + + return fusion_entry_create( &dev->shmpool, &pool->pool_id, pool ); +} + +int +fusion_shmpool_attach (FusionDev *dev, + FusionSHMPoolAttach *attach, + FusionID fusion_id) +{ + int ret; + SHMPoolNode *node; + FusionSHMPool *shmpool; + + ret = fusion_shmpool_lock( &dev->shmpool, attach->pool_id, false, &shmpool ); + if (ret) + return ret; + + dev->stat.shmpool_attach++; + + node = get_node (shmpool, fusion_id); + if (!node) { + node = kmalloc (sizeof(SHMPoolNode), GFP_KERNEL); + if (!node) { + fusion_shmpool_unlock( shmpool ); + return -ENOMEM; + } + + node->fusion_id = fusion_id; + node->count = 1; + + fusion_list_prepend (&shmpool->nodes, &node->link); + } + else + node->count++; + + attach->addr_base = shmpool->addr_base; + attach->size = shmpool->size; + + fusion_shmpool_unlock( shmpool ); + + return 0; +} + +int +fusion_shmpool_detach (FusionDev *dev, int id, FusionID fusion_id) +{ + int ret; + SHMPoolNode *node; + FusionSHMPool *shmpool; + + ret = fusion_shmpool_lock( &dev->shmpool, id, false, &shmpool ); + if (ret) + return ret; + + dev->stat.shmpool_detach++; + + node = get_node (shmpool, fusion_id); + if (!node) { + fusion_shmpool_unlock( shmpool ); + return -EIO; + } + + if (! --node->count) { + fusion_list_remove (&shmpool->nodes, &node->link); + kfree (node); + } + + fusion_shmpool_unlock( shmpool ); + + return 0; +} + +int +fusion_shmpool_dispatch( FusionDev *dev, + FusionSHMPoolDispatch *dispatch, + Fusionee *fusionee ) +{ + int ret; + FusionLink *l; + FusionSHMPool *shmpool; + FusionSHMPoolMessage message; + FusionID fusion_id = fusionee_id( fusionee ); + + if (dispatch->size <= 0) + return -EINVAL; + + ret = fusion_shmpool_lock( &dev->shmpool, dispatch->pool_id, false, &shmpool ); + if (ret) + return ret; + + message.type = FSMT_REMAP; + message.size = dispatch->size; + + shmpool->dispatch_count++; + + shmpool->size = dispatch->size; + + fusion_list_foreach (l, shmpool->nodes) { + SHMPoolNode *node = (SHMPoolNode *) l; + + if (node->fusion_id == fusion_id) + continue; + + fusionee_send_message (dev, fusionee, node->fusion_id, FMT_SHMPOOL, + shmpool->entry.id, sizeof(message), &message); + } + + fusion_shmpool_unlock( shmpool ); + + return 0; +} + +int +fusion_shmpool_destroy (FusionDev *dev, int id) +{ + return fusion_entry_destroy( &dev->shmpool, id ); +} + +void +fusion_shmpool_detach_all (FusionDev *dev, FusionID fusion_id) +{ + FusionLink *l; + + down (&dev->shmpool.lock); + + fusion_list_foreach (l, dev->shmpool.list) { + FusionSHMPool *shmpool = (FusionSHMPool *) l; + + remove_node (shmpool, fusion_id); + } + + up (&dev->shmpool.lock); +} + +int +fusion_shmpool_fork_all( FusionDev *dev, + FusionID fusion_id, + FusionID from_id ) +{ + FusionLink *l; + int ret = 0; + + down (&dev->shmpool.lock); + + fusion_list_foreach (l, dev->shmpool.list) { + FusionSHMPool *shmpool = (FusionSHMPool *) l; + + ret = fork_node( shmpool, fusion_id, from_id ); + if (ret) + break; + } + + up (&dev->shmpool.lock); + + return ret; +} + +/******************************************************************************/ + +static SHMPoolNode * +get_node (FusionSHMPool *shmpool, + FusionID fusion_id) +{ + SHMPoolNode *node; + + fusion_list_foreach (node, shmpool->nodes) { + if (node->fusion_id == fusion_id) + return node; + } + + return NULL; +} + +static void +remove_node (FusionSHMPool *shmpool, FusionID fusion_id) +{ + SHMPoolNode *node; + + down (&shmpool->entry.lock); + + fusion_list_foreach (node, shmpool->nodes) { + if (node->fusion_id == fusion_id) { + fusion_list_remove (&shmpool->nodes, &node->link); + break; + } + } + + up (&shmpool->entry.lock); +} + +static int +fork_node (FusionSHMPool *shmpool, FusionID fusion_id, FusionID from_id) +{ + int ret = 0; + SHMPoolNode *node; + + down (&shmpool->entry.lock); + + fusion_list_foreach (node, shmpool->nodes) { + if (node->fusion_id == from_id) { + SHMPoolNode *new_node; + + new_node = kmalloc (sizeof(SHMPoolNode), GFP_KERNEL); + if (!new_node) { + ret = -ENOMEM; + break; + } + + new_node->fusion_id = fusion_id; + new_node->count = node->count; + + fusion_list_prepend (&shmpool->nodes, &new_node->link); + + break; + } + } + + up (&shmpool->entry.lock); + + return ret; +} + +static void +free_all_nodes (FusionSHMPool *shmpool) + +{ + FusionLink *n; + SHMPoolNode *node; + + fusion_list_foreach_safe (node, n, shmpool->nodes) { + kfree (node); + } + + shmpool->nodes = NULL; +} diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/shmpool.h linux-2.6.22.1/drivers/char/fusion/shmpool.h --- linux-2.6.22.1-0rig/drivers/char/fusion/shmpool.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/shmpool.h 2007-01-20 05:03:01.000000000 +0100 @@ -0,0 +1,59 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSION__SHMPOOL_H__ +#define __FUSION__SHMPOOL_H__ + +#include "fusiondev.h" +#include "types.h" + + +/* module init/cleanup */ + +int fusion_shmpool_init (FusionDev *dev); +void fusion_shmpool_deinit (FusionDev *dev); + + +/* public API */ + +int fusion_shmpool_new (FusionDev *dev, + FusionSHMPoolNew *pool); + +int fusion_shmpool_attach (FusionDev *dev, + FusionSHMPoolAttach *attach, + FusionID fusion_id); + +int fusion_shmpool_detach (FusionDev *dev, + int id, + FusionID fusion_id); + +int fusion_shmpool_dispatch (FusionDev *dev, + FusionSHMPoolDispatch *dispatch, + Fusionee *fusionee ); + +int fusion_shmpool_destroy (FusionDev *dev, + int id); + + +/* internal functions */ + +void fusion_shmpool_detach_all (FusionDev *dev, + FusionID fusion_id); + +int fusion_shmpool_fork_all (FusionDev *dev, + FusionID fusion_id, + FusionID from_id); + +#endif + diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/skirmish.c linux-2.6.22.1/drivers/char/fusion/skirmish.c --- linux-2.6.22.1-0rig/drivers/char/fusion/skirmish.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/skirmish.c 2007-01-06 14:09:45.000000000 +0100 @@ -0,0 +1,397 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifdef HAVE_LINUX_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include + +#include + +#include "fusiondev.h" +#include "fusionee.h" +#include "list.h" +#include "skirmish.h" + + +#define MAX_PRE_ACQUISITIONS 32 + + +typedef struct __FUSION_FusionSkirmish FusionSkirmish; + +struct __FUSION_FusionSkirmish { + FusionEntry entry; + + int lock_fid; /* non-zero if locked */ + int lock_pid; + int lock_count; + + int lock_total; + +#ifdef FUSION_DEBUG_SKIRMISH_DEADLOCK + int pre_acquis[MAX_PRE_ACQUISITIONS]; + + bool outer; +#endif +}; + +static int +fusion_skirmish_print( FusionEntry *entry, + void *ctx, + char *buf ) +{ + int written = 0; + FusionSkirmish *skirmish = (FusionSkirmish*) entry; + +#ifdef FUSION_DEBUG_SKIRMISH_DEADLOCK + int i, n; + + + for (i=0, n=0; ipre_acquis[i]) { + n++; + } + } + + written += sprintf( buf + written, "[%2d]%s", n, skirmish->outer ? "." : " " ); + + for (i=0, n=0; ipre_acquis[i]) { + written += sprintf( buf + written, "%s%02x", n ? "," : "", skirmish->pre_acquis[i] - 1 ); + + n++; + } + } +#endif + + if (skirmish->lock_fid) { + if (skirmish->entry.waiters) + return sprintf( buf + written, " - %dx [0x%08x] (%d) %d WAITING\n", + skirmish->lock_count, skirmish->lock_fid, + skirmish->lock_pid, skirmish->entry.waiters ) + written; + else + return sprintf( buf + written, " - %dx [0x%08x] (%d)\n", + skirmish->lock_count, skirmish->lock_fid, + skirmish->lock_pid ) + written; + } + + return sprintf( buf + written, "\n" ) + written; +} + +FUSION_ENTRY_CLASS( FusionSkirmish, skirmish, NULL, NULL, fusion_skirmish_print ) + +/******************************************************************************/ + +int +fusion_skirmish_init (FusionDev *dev) +{ + fusion_entries_init( &dev->skirmish, &skirmish_class, dev ); + + create_proc_read_entry( "skirmishs", 0, dev->proc_dir, + fusion_entries_read_proc, &dev->skirmish ); + + return 0; +} + +void +fusion_skirmish_deinit (FusionDev *dev) +{ + remove_proc_entry ("skirmishs", dev->proc_dir); + + fusion_entries_deinit( &dev->skirmish ); +} + +/******************************************************************************/ + +int +fusion_skirmish_new (FusionDev *dev, int *ret_id) +{ + return fusion_entry_create( &dev->skirmish, ret_id, NULL ); +} + +int +fusion_skirmish_prevail (FusionDev *dev, int id, int fusion_id) +{ + int ret; + FusionSkirmish *skirmish; +#ifdef FUSION_DEBUG_SKIRMISH_DEADLOCK + FusionSkirmish *s; + int i; + bool outer = true; +#endif + + dev->stat.skirmish_prevail_swoop++; + + ret = fusion_skirmish_lock( &dev->skirmish, id, true, &skirmish ); + if (ret) + return ret; + + if (skirmish->lock_pid == current->pid) { + skirmish->lock_count++; + skirmish->lock_total++; + fusion_skirmish_unlock( skirmish ); + up( &dev->skirmish.lock ); + return 0; + } + +#ifdef FUSION_DEBUG_SKIRMISH_DEADLOCK + /* look in currently acquired skirmishs for this one being + a pre-acquisition, indicating a potential deadlock */ + fusion_list_foreach (s, dev->skirmish.list) { + if (s->lock_pid != current->pid) + continue; + + outer = false; + + for (i=0; ipre_acquis[i] == id + 1) { + printk( KERN_DEBUG "FusionSkirmish: Potential deadlock " + "between locked 0x%x and to be locked 0x%x in world %d!\n", + s->entry.id, skirmish->entry.id, dev->index ); + } + } + } + + if (outer) + skirmish->outer = true; + + /* remember all previously acquired skirmishs being pre-acquisitions for + this one, to detect potential deadlocks due to a lock order twist */ + fusion_list_foreach (s, dev->skirmish.list) { + int free = -1; + + if (s->lock_pid != current->pid) + continue; + + for (i=0; ipre_acquis[i]) { + if (skirmish->pre_acquis[i] == s->entry.id + 1) { + break; + } + } + else + free = i; + } + + /* not found? */ + if (i == MAX_PRE_ACQUISITIONS) { + if (free != -1) { + skirmish->pre_acquis[free] = s->entry.id + 1; + } + else { + printk( KERN_DEBUG "FusionSkirmish: Too many pre-acquisitions to remember.\n" ); + + printk( KERN_DEBUG " [ '%s' ] <- ", skirmish->entry.name ); + + for (i=0; ipre_acquis[i] - 1 ); + + printk( "\n" ); + } + } + } +#endif + + up( &dev->skirmish.lock ); + + while (skirmish->lock_pid) { + ret = fusion_skirmish_wait( skirmish, NULL ); + if (ret) + return ret; + } + + skirmish->lock_fid = fusion_id; + skirmish->lock_pid = current->pid; + skirmish->lock_count = 1; + + skirmish->lock_total++; + + fusion_skirmish_unlock( skirmish ); + + return 0; +} + +int +fusion_skirmish_swoop (FusionDev *dev, int id, int fusion_id) +{ + int ret; + FusionSkirmish *skirmish; + + ret = fusion_skirmish_lock( &dev->skirmish, id, false, &skirmish ); + if (ret) + return ret; + + dev->stat.skirmish_prevail_swoop++; + + if (skirmish->lock_fid) { + if (skirmish->lock_pid == current->pid) { + skirmish->lock_count++; + skirmish->lock_total++; + fusion_skirmish_unlock( skirmish ); + return 0; + } + + fusion_skirmish_unlock( skirmish ); + + return -EAGAIN; + } + + skirmish->lock_fid = fusion_id; + skirmish->lock_pid = current->pid; + skirmish->lock_count = 1; + + skirmish->lock_total++; + + fusion_skirmish_unlock( skirmish ); + + return 0; +} + +int +fusion_skirmish_lock_count (FusionDev *dev, int id, int fusion_id, int *ret_lock_count) +{ + int ret; + FusionSkirmish *skirmish; + + ret = fusion_skirmish_lock( &dev->skirmish, id, false, &skirmish ); + if (ret) + return ret; + + if (skirmish->lock_fid == fusion_id && + skirmish->lock_pid == current->pid) + { + *ret_lock_count = skirmish->lock_count; + } + else + { + *ret_lock_count = 0; + } + + fusion_skirmish_unlock( skirmish ); + + return 0; +} + +int +fusion_skirmish_dismiss (FusionDev *dev, int id, int fusion_id) +{ + int ret; + FusionSkirmish *skirmish; + + ret = fusion_skirmish_lock( &dev->skirmish, id, false, &skirmish ); + if (ret) + return ret; + + dev->stat.skirmish_dismiss++; + + if (skirmish->lock_pid != current->pid) { + fusion_skirmish_unlock( skirmish ); + return -EIO; + } + + if (--skirmish->lock_count == 0) { + skirmish->lock_fid = 0; + skirmish->lock_pid = 0; + + fusion_skirmish_notify( skirmish, true ); + } + + fusion_skirmish_unlock( skirmish ); + + return 0; +} + +int +fusion_skirmish_destroy (FusionDev *dev, int id) +{ +#ifdef FUSION_DEBUG_SKIRMISH_DEADLOCK + int i; + FusionSkirmish *s; + + /* Lock entries. */ + if (down_interruptible( &dev->skirmish.lock )) + return -EINTR; + + /* remove from all pre-acquisition lists */ + fusion_list_foreach (s, dev->skirmish.list) { + for (i=0; ipre_acquis[i] == id + 1) + s->pre_acquis[i] = 0; + } + } + + up( &dev->skirmish.lock ); + + /* FIXME: gap? */ +#endif + + return fusion_entry_destroy( &dev->skirmish, id ); +} + +void +fusion_skirmish_dismiss_all (FusionDev *dev, int fusion_id) +{ + FusionLink *l; + + down (&dev->skirmish.lock); + + fusion_list_foreach (l, dev->skirmish.list) { + FusionSkirmish *skirmish = (FusionSkirmish *) l; + + down (&skirmish->entry.lock); + + if (skirmish->lock_fid == fusion_id) { + skirmish->lock_fid = 0; + skirmish->lock_pid = 0; + skirmish->lock_count = 0; + + wake_up_interruptible_all (&skirmish->entry.wait); + } + + up (&skirmish->entry.lock); + } + + up (&dev->skirmish.lock); +} + +void +fusion_skirmish_dismiss_all_from_pid (FusionDev *dev, int pid) +{ + FusionLink *l; + + down (&dev->skirmish.lock); + + fusion_list_foreach (l, dev->skirmish.list) { + FusionSkirmish *skirmish = (FusionSkirmish *) l; + + down (&skirmish->entry.lock); + + if (skirmish->lock_pid == pid) { + skirmish->lock_fid = 0; + skirmish->lock_pid = 0; + skirmish->lock_count = 0; + + wake_up_interruptible_all (&skirmish->entry.wait); + } + + up (&skirmish->entry.lock); + } + + up (&dev->skirmish.lock); +} + diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/skirmish.h linux-2.6.22.1/drivers/char/fusion/skirmish.h --- linux-2.6.22.1-0rig/drivers/char/fusion/skirmish.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/skirmish.h 2006-06-30 10:54:55.000000000 +0200 @@ -0,0 +1,62 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002-2003 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSION__SKIRMISH_H__ +#define __FUSION__SKIRMISH_H__ + +#include "fusiondev.h" +#include "types.h" + + +/* module init/cleanup */ + +int fusion_skirmish_init (FusionDev *dev); +void fusion_skirmish_deinit (FusionDev *dev); + + +/* public API */ + +int fusion_skirmish_new (FusionDev *dev, + int *id); + +int fusion_skirmish_prevail (FusionDev *dev, + int id, + int fusion_id); + +int fusion_skirmish_swoop (FusionDev *dev, + int id, + int fusion_id); + +int fusion_skirmish_lock_count (FusionDev *dev, + int id, + int fusion_id, + int *ret_lock_count); + +int fusion_skirmish_dismiss (FusionDev *dev, + int id, + int fusion_id); + +int fusion_skirmish_destroy (FusionDev *dev, + int id); + + +/* internal functions */ + +void fusion_skirmish_dismiss_all (FusionDev *dev, + int fusion_id); + +void fusion_skirmish_dismiss_all_from_pid (FusionDev *dev, + int pid); + +#endif diff -urN linux-2.6.22.1-0rig/drivers/char/fusion/types.h linux-2.6.22.1/drivers/char/fusion/types.h --- linux-2.6.22.1-0rig/drivers/char/fusion/types.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/drivers/char/fusion/types.h 2007-01-20 05:03:01.000000000 +0100 @@ -0,0 +1,29 @@ +/* + * Fusion Kernel Module + * + * (c) Copyright 2002 Convergence GmbH + * + * Written by Denis Oliver Kropp + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __FUSION__TYPES_H__ +#define __FUSION__TYPES_H__ + +#include + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) +typedef enum { + false = 0, + true = !false +} bool; +#endif + +typedef struct __Fusion_Fusionee Fusionee; + +#endif diff -urN linux-2.6.22.1-0rig/drivers/char/Kconfig linux-2.6.22.1/drivers/char/Kconfig --- linux-2.6.22.1-0rig/drivers/char/Kconfig 2007-07-10 20:56:30.000000000 +0200 +++ linux-2.6.22.1/drivers/char/Kconfig 2007-08-12 19:30:42.000000000 +0200 @@ -484,6 +484,8 @@ All modern Linux systems use the Unix98 ptys. Say Y unless you're on an embedded system and want to conserve memory. +source "drivers/char/fusion/Kconfig" + config LEGACY_PTYS bool "Legacy (BSD) PTY support" default y diff -urN linux-2.6.22.1-0rig/drivers/char/Makefile linux-2.6.22.1/drivers/char/Makefile --- linux-2.6.22.1-0rig/drivers/char/Makefile 2007-07-10 20:56:30.000000000 +0200 +++ linux-2.6.22.1/drivers/char/Makefile 2007-08-12 19:45:20.000000000 +0200 @@ -100,6 +100,7 @@ obj-$(CONFIG_DRM) += drm/ obj-$(CONFIG_PCMCIA) += pcmcia/ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ +obj-$(CONFIG_FUSION_DEVICE) += fusion/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ diff -urN linux-2.6.22.1-0rig/include/linux/fusion.h linux-2.6.22.1/include/linux/fusion.h --- linux-2.6.22.1-0rig/include/linux/fusion.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.22.1/include/linux/fusion.h 2007-01-29 01:43:50.000000000 +0100 @@ -0,0 +1,277 @@ +#ifndef __LINUX__FUSION_H__ +#define __LINUX__FUSION_H__ + +#include + +/* + * Fusion Kernel Device API Version + */ +#define FUSION_API_MAJOR 3 /* Increased if backward compatibility is dropped. */ +#define FUSION_API_MINOR 2 /* Increased if new features are added. */ + +/* + * The Fusion ID is a unique identifier for one process consisting of threads. + */ +typedef unsigned long FusionID; + +#define FUSION_ID_MASTER 1 /* This is the fusion id of the master (first process). */ + +/* + * Entering a world + */ +typedef struct { + struct { + int major; /* Must be set to FUSION_API_MAJOR before entering. */ + int minor; /* Must be set to FUSION_API_MINOR before entering. */ + } api; + + FusionID fusion_id; /* Returns the fusion id of the entering process. */ +} FusionEnter; + +/* + * Forking in world + */ +typedef struct { + FusionID fusion_id; /* Returns the fusion id of the new (forked) fusionee. */ +} FusionFork; + +/* + * Sending a message + */ +typedef struct { + FusionID fusion_id; /* recipient */ + + int msg_id; /* optional message identifier */ + int msg_size; /* message size, must be greater than zero */ + const void *msg_data; /* message data, must not be NULL */ +} FusionSendMessage; + +/* + * Receiving a message + */ +typedef enum { + FMT_SEND, /* msg_id is an optional custom id */ + FMT_CALL, /* msg_id is the call id */ + FMT_REACTOR, /* msg_id is the reactor id */ + FMT_SHMPOOL /* msg_id is the pool id */ +} FusionMessageType; + +typedef struct { + FusionMessageType msg_type; /* type (origin) of message */ + + int msg_id; /* message id (custom id or call/reactor/pool id) */ + int msg_size; /* size of the following message data */ + + /* message data follows */ +} FusionReadMessage; + +/* + * Dispatching a message via a reactor + */ +typedef struct { + int reactor_id; + int self; + + int msg_size; /* message size, must be greater than zero */ + const void *msg_data; /* message data, must not be NULL */ +} FusionReactorDispatch; + +/* + * Calling (synchronous RPC) + */ +typedef struct { + int call_id; /* new call id returned */ + + void *handler; /* function pointer of handler to install */ + void *ctx; /* optional handler context */ +} FusionCallNew; + +typedef enum { + FCEF_NONE = 0x00000000, + FCEF_ONEWAY = 0x00000001, + FCEF_ALL = 0x00000001 +} FusionCallExecFlags; + +typedef struct { + int ret_val; /* return value of the call */ + + int call_id; /* id of the requested call, each call has a fixed owner */ + + int call_arg; /* optional int argument */ + void *call_ptr; /* optional pointer argument (shared memory) */ + + FusionCallExecFlags flags; /* execution flags */ +} FusionCallExecute; + +typedef struct { + int call_id; /* id of currently executing call */ + + int val; /* value to return */ +} FusionCallReturn; + +typedef struct { + void *handler; /* function pointer of handler to call */ + void *ctx; /* optional handler context */ + + int caller; /* fusion id of the caller or zero if called from Fusion */ + int call_arg; /* optional call parameter */ + void *call_ptr; /* optional call parameter */ +} FusionCallMessage; + +/* + * Watching a reference + * + * This information is needed to have a specific call being executed if the + * reference count reaches zero. Currently one watch per reference is allowed. + * + * The call is made by Fusion and therefor has a caller id of zero. + * + */ +typedef struct { + int id; /* id of the reference to watch */ + + int call_id; /* id of the call to execute */ + int call_arg; /* optional call parameter, e.g. the id of a user + space resource associated with that reference */ +} FusionRefWatch; + +/* + * Inheriting local count from other reference + */ +typedef struct { + int id; /* own reference id */ + int from; /* id of the reference to inherit from */ +} FusionRefInherit; + +/* + * Killing other fusionees (experimental) + */ +typedef struct { + FusionID fusion_id; /* fusionee to kill, zero means all but ourself */ + int signal; /* signal to be delivered, e.g. SIGTERM */ + int timeout_ms; /* -1 means no timeout, 0 means infinite, otherwise the + max. time to wait until the fusionee(s) terminated */ +} FusionKill; + + +/* + * Shared memory pools + */ +typedef struct { + int max_size; /* Maximum size that this pool will be allowed to grow to. */ + + int pool_id; /* Returns the new pool id. */ + void *addr_base; /* Returns the base of the reserved virtual memory address space. */ +} FusionSHMPoolNew; + +typedef struct { + int pool_id; /* The id of the pool to attach to. */ + + void *addr_base; /* Returns the base of the reserved virtual memory address space. */ + int size; /* Returns the current size of the pool. */ +} FusionSHMPoolAttach; + +typedef struct { + int pool_id; /* The id of the pool to notify. */ + + int size; /* New size of the pool. */ +} FusionSHMPoolDispatch; + +typedef enum { + FSMT_REMAP, /* Remap the pool due to a change of its size. */ + FSMT_UNMAP /* Unmap the pool due to its destruction. */ +} FusionSHMPoolMessageType; + +typedef struct { + FusionSHMPoolMessageType type; /* Type of the message. */ + + int size; /* New size of the pool, if type is FSMT_REMAP. */ +} FusionSHMPoolMessage; + + +/* + * Fusion types + */ +typedef enum { + FT_LOUNGE, + FT_MESSAGING, + FT_CALL, + FT_REF, + FT_SKIRMISH, + FT_PROPERTY, + FT_REACTOR, + FT_SHMPOOL +} FusionType; + + +/* + * Set attributes like 'name' for an entry of the specified type. + */ +#define FUSION_ENTRY_INFO_NAME_LENGTH 24 + +typedef struct { + FusionType type; + int id; + + char name[FUSION_ENTRY_INFO_NAME_LENGTH]; +} FusionEntryInfo; + + + +#define FUSION_ENTER _IOR(FT_LOUNGE, 0x00, FusionEnter) +#define FUSION_UNBLOCK _IO (FT_LOUNGE, 0x01) +#define FUSION_KILL _IOW(FT_LOUNGE, 0x02, FusionKill) + +#define FUSION_ENTRY_SET_INFO _IOW(FT_LOUNGE, 0x03, FusionEntryInfo) +#define FUSION_ENTRY_GET_INFO _IOW(FT_LOUNGE, 0x04, FusionEntryInfo) + +#define FUSION_FORK _IOW(FT_LOUNGE, 0x05, FusionFork) + +#define FUSION_SEND_MESSAGE _IOW(FT_MESSAGING, 0x00, FusionSendMessage) + +#define FUSION_CALL_NEW _IOW(FT_CALL, 0x00, FusionCallNew) +#define FUSION_CALL_EXECUTE _IOW(FT_CALL, 0x01, FusionCallExecute) +#define FUSION_CALL_RETURN _IOW(FT_CALL, 0x02, FusionCallReturn) +#define FUSION_CALL_DESTROY _IOW(FT_CALL, 0x03, int) + +#define FUSION_REF_NEW _IOW(FT_REF, 0x00, int) +#define FUSION_REF_UP _IOW(FT_REF, 0x01, int) +#define FUSION_REF_UP_GLOBAL _IOW(FT_REF, 0x02, int) +#define FUSION_REF_DOWN _IOW(FT_REF, 0x03, int) +#define FUSION_REF_DOWN_GLOBAL _IOW(FT_REF, 0x04, int) +#define FUSION_REF_ZERO_LOCK _IOW(FT_REF, 0x05, int) +#define FUSION_REF_ZERO_TRYLOCK _IOW(FT_REF, 0x06, int) +#define FUSION_REF_UNLOCK _IOW(FT_REF, 0x07, int) +#define FUSION_REF_STAT _IOW(FT_REF, 0x08, int) +#define FUSION_REF_WATCH _IOW(FT_REF, 0x09, FusionRefWatch) +#define FUSION_REF_INHERIT _IOW(FT_REF, 0x0A, FusionRefInherit) +#define FUSION_REF_DESTROY _IOW(FT_REF, 0x0B, int) + +#define FUSION_SKIRMISH_NEW _IOW(FT_SKIRMISH, 0x00, int) +#define FUSION_SKIRMISH_PREVAIL _IOW(FT_SKIRMISH, 0x01, int) +#define FUSION_SKIRMISH_SWOOP _IOW(FT_SKIRMISH, 0x02, int) +#define FUSION_SKIRMISH_DISMISS _IOW(FT_SKIRMISH, 0x03, int) +#define FUSION_SKIRMISH_DESTROY _IOW(FT_SKIRMISH, 0x04, int) +#define FUSION_SKIRMISH_LOCK_COUNT _IOW(FT_SKIRMISH, 0x05, int) + +#define FUSION_PROPERTY_NEW _IOW(FT_PROPERTY, 0x00, int) +#define FUSION_PROPERTY_LEASE _IOW(FT_PROPERTY, 0x01, int) +#define FUSION_PROPERTY_PURCHASE _IOW(FT_PROPERTY, 0x02, int) +#define FUSION_PROPERTY_CEDE _IOW(FT_PROPERTY, 0x03, int) +#define FUSION_PROPERTY_HOLDUP _IOW(FT_PROPERTY, 0x04, int) +#define FUSION_PROPERTY_DESTROY _IOW(FT_PROPERTY, 0x05, int) + +#define FUSION_REACTOR_NEW _IOW(FT_REACTOR, 0x00, int) +#define FUSION_REACTOR_ATTACH _IOW(FT_REACTOR, 0x01, int) +#define FUSION_REACTOR_DETACH _IOW(FT_REACTOR, 0x02, int) +#define FUSION_REACTOR_DISPATCH _IOW(FT_REACTOR, 0x03, FusionReactorDispatch) +#define FUSION_REACTOR_DESTROY _IOW(FT_REACTOR, 0x04, int) + +#define FUSION_SHMPOOL_NEW _IOW(FT_SHMPOOL, 0x00, FusionSHMPoolNew) +#define FUSION_SHMPOOL_ATTACH _IOW(FT_SHMPOOL, 0x01, FusionSHMPoolAttach) +#define FUSION_SHMPOOL_DETACH _IOW(FT_SHMPOOL, 0x02, int) +#define FUSION_SHMPOOL_DISPATCH _IOW(FT_SHMPOOL, 0x03, FusionSHMPoolDispatch) +#define FUSION_SHMPOOL_DESTROY _IOW(FT_SHMPOOL, 0x04, int) + +#endif +