From: Ulf Samuelsson Date: Sun, 12 Aug 2007 18:19:45 +0000 (-0000) Subject: Add support for fusion linux module X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=dcab96b108ce577894e176be92b9b268c7a40d67;p=buildroot.git Add support for fusion linux module --- diff --git a/package/Config.in b/package/Config.in index c39adbcdee..d3b87796ff 100644 --- a/package/Config.in +++ b/package/Config.in @@ -279,6 +279,7 @@ source "package/dialog/Config.in" comment "graphic libraries" source "package/directfb/Config.in" +source "package/fusion/Config.in" source "package/imagemagick/Config.in" source "package/jpeg/Config.in" source "package/libpng/Config.in" diff --git a/package/fusion/40-fusion.rules b/package/fusion/40-fusion.rules new file mode 100644 index 0000000000..dc7fca19ca --- /dev/null +++ b/package/fusion/40-fusion.rules @@ -0,0 +1 @@ +KERNEL=="fusion[0-9]*", NAME="fusion/%n", GROUP="video", MODE="0660" diff --git a/package/fusion/Config.in b/package/fusion/Config.in new file mode 100644 index 0000000000..7591199b35 --- /dev/null +++ b/package/fusion/Config.in @@ -0,0 +1,7 @@ +config BR2_PACKAGE_LINUX_FUSION + bool "linux-fusion communication layer for DirectFB multi" + default n + depends on BR2_PACKAGE_DIRECTFB + help + DirectFB Communication Layer allowing multiple DirectFB + applications to run concurrently diff --git a/package/fusion/fusion.h b/package/fusion/fusion.h new file mode 100644 index 0000000000..d9139fc6d4 --- /dev/null +++ b/package/fusion/fusion.h @@ -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 + diff --git a/package/fusion/fusion.mk b/package/fusion/fusion.mk new file mode 100644 index 0000000000..ec1e40bf6c --- /dev/null +++ b/package/fusion/fusion.mk @@ -0,0 +1,41 @@ +############################################################# +# +# linux-fusion +# +############################################################# +LINUX_FUSION_VERSION:=3.2.3 +LINUX_FUSION_SOURCE:=linux-fusion-$(LINUX_FUSION_VERSION).tar.gz +LINUX_FUSION_SITE:=http://www.directfb.org/downloads/Core +LINUX_FUSION_CAT:=$(ZCAT) +LINUX_FUSION_DIR:=$(TARGET_DIR)/etc/udev/rules.d +LINUX_FUSION:=40-fusion.rules +LINUX_FUSION_HEADER=$(STAGING_DIR)/usr/include/linux/fusion.h + +############################################################# +# +# build linux-fusion +# +############################################################# + +$(LINUX_FUSION_HEADER): + cp -dpf package/fusion/fusion.h $(LINUX_FUSION_HEADER) + +$(LINUX_FUSION_DIR)/$(LINUX_FUSION): + mkdir -p $(LINUX_FUSION_DIR) + cp -dpf package/fusion/40-fusion.rules $(LINUX_FUSION_DIR) + touch -c $@ + +linux-fusion: $(LINUX_FUSION_DIR)/$(LINUX_FUSION) $(LINUX_FUSION_HEADER) + +linux-fusion-clean: + rm -f $(LINUX_FUSION_DIR)/$(LINUX_FUSION)) + +############################################################# +# +# Toplevel Makefile options +# +############################################################# +ifeq ($(strip $(BR2_PACKAGE_LINUX_FUSION)),y) +TARGETS+=linux-fusion +endif + diff --git a/package/fusion/linux-2.6.22.1-008-fusion.patch b/package/fusion/linux-2.6.22.1-008-fusion.patch new file mode 100644 index 0000000000..81d98afada --- /dev/null +++ b/package/fusion/linux-2.6.22.1-008-fusion.patch @@ -0,0 +1,6265 @@ +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 ++